UNPKG

svg-bbox

Version:

A set of tools to compute and to use a SVG bounding box you can trust (as opposed to the unreliable .getBBox() scourge)

2 lines • 24.7 kB
/*! SvgVisualBBox.js v1.0.10 | MIT License | https://github.com/Emasoft/SVG-BBOX */ !function(e,t){"function"==typeof define&&define.amd?define([],t):"object"==typeof module&&module.exports?module.exports=t():e.SvgVisualBBox=t()}("undefined"!=typeof self?self:this,()=>{"use strict";function e(e){return e&&/^auto_id_/.test(e)}function t(t){if(!t)return"null/undefined element";const n=[],i=t.tagName||t.nodeName||"unknown";if(n.push(`<${i}>`),t.id){const i=e(t.id)?`id="${t.id}" (AUTO-GENERATED - not in original SVG!)`:`id="${t.id}"`;n.push(i)}t.getAttribute&&t.getAttribute("class")&&n.push(`class="${t.getAttribute("class")}"`);const o=t.parentNode;if(o&&o instanceof Element){const t=o.id?e(o.id)?` id="${o.id}" (AUTO-GENERATED)`:` id="${o.id}"`:"";n.push(`(child of <${o.tagName}>${t})`)}return n.join(" ")}function n(t,n){const i=t&&t.id&&e(t.id),o=t&&t.parentNode&&t.parentNode instanceof Element&&t.parentNode.id&&e(t.parentNode.id),s=n&&n.id&&e(n.id);if(!i&&!o&&!s)return"";!function(e){if(e)try{const t=(new XMLSerializer).serializeToString(e),n=(new Date).toISOString().replace(/[:.]/g,"-").replace("T","_").slice(0,-5),i=e.id||"svg";"undefined"!=typeof window&&(window.__DEBUG_SVG_DATA__={content:t,filename:`${i}_debug_${n}.svg`,timestamp:n})}catch(e){}}(n);const r=t&&t.id?t.id:"(none)",a=(new Date).toISOString().replace(/[:.]/g,"-").replace("T","_").slice(0,-5);return`\nāš ļø AUTO-GENERATED ID WARNING:\n The IDs shown above were automatically assigned by sbb-extractor.cjs.\n These IDs DO NOT EXIST in your original SVG file!\n\n šŸ” DEBUG SVG WILL BE AUTOMATICALLY SAVED:\n ${`${n&&n.id?n.id:"svg"}_debug_${a}.svg`}\n\n To find this element in your original SVG:\n 1. Open the debug SVG file (saved automatically in current directory)\n 2. Search for the ID "${r}" to locate the problematic element\n 3. Note the element's position, visual appearance, and attributes\n 4. Find the corresponding element in your original SVG using these details\n`}function i(e){if(!e||"undefined"==typeof window||!window.getComputedStyle)return"unknown";try{const t=window.getComputedStyle(e);return t.fontFamily||t.getPropertyValue("font-family")||"default"}catch{return"error detecting font"}}async function waitForDocumentFonts(e,t){e||(e=document),"number"!=typeof t&&(t=8e3);const n=e.fonts;if(!n||!n.ready)return;const i=n.ready;t<=0?await i:await Promise.race([i,new Promise(e=>setTimeout(e,t))])}function o(e,t,n){const i=t.width/n.width,o=t.height/n.height,s=(e.getAttribute("preserveAspectRatio")||"xMidYMid meet").trim().split(/\s+/).filter(e=>"defer"!==e);if(1===s.length&&"none"===s[0])return{scaleX:i,scaleY:o,offsetX:0,offsetY:0,uniform:!1};const r=s[0]||"xMidYMid";let a;a="slice"===(s[1]||"meet")?Math.min(i,o):Math.max(i,o);const l=t.width/a,d=t.height/a,h=n.width-l,c=n.height-d;let u=0,g=0;return r.startsWith("xMid")?u=h/2:r.startsWith("xMax")&&(u=h),r.includes("YMid")?g=c/2:r.includes("YMax")&&(g=c),{scale:a,offsetX:u,offsetY:g,uniform:!0}}async function s(e,o,s,r){if(!s||s.width<=0||s.height<=0)return null;const a=s,l=!!e.id;let d;l||(d="__svg_visual_bbox_tmp_"+Math.random().toString(36).slice(2),e.id=d);const h=o.cloneNode(!0);l||e.removeAttribute("id"),h.getAttribute("xmlns")||h.setAttribute("xmlns","http://www.w3.org/2000/svg"),h.setAttribute("viewBox",a.x+" "+a.y+" "+a.width+" "+a.height);const c=16384,u=Math.max(1,Math.round(a.width*r)),g=Math.max(1,Math.round(a.height*r));let f,m=r,y=u,w=g;if(u>c||g>c){const e=c/u,t=c/g,n=Math.min(e,t);m=r*n,y=Math.max(1,Math.round(a.width*m)),w=Math.max(1,Math.round(a.height*m))}if(h.setAttribute("width",String(y)),h.setAttribute("height",String(w)),h.setAttribute("preserveAspectRatio","none"),h.removeAttribute("x"),h.removeAttribute("y"),e===o)f=h;else{const t=d||e.id;f=h.getElementById(t)}if(!f){const i=t(e),s=d?`Temporary ID used: "${d}"`:"Element ID: "+(e.id?`"${e.id}"`:"(none)"),r=n(e,o);throw new Error(`āŒ Cannot render SVG element: Element not found in cloned SVG\n\nELEMENT DETAILS:\n ${i}\n ${s}\n SVG Root: ${o.id?`id="${o.id}"`:"(no id)"}\n`+r+"\nThis typically happens when:\n 1. The element IS the SVG root itself (not supported - query a child element instead)\n 2. The element was removed or modified during cloning\n 3. The element's ID conflicts with another element\n\nHow to fix:\n • If querying the root <svg>, query a child element instead\n • Ensure the element has a unique 'id' attribute\n • Check that the element exists in the DOM before calling this function\n • Verify the element hasn't been dynamically removed")}const p=new Set;let x=f;for(;x&&(p.add(x),x!==h);)x=x.parentNode;!function e(t){p.add(t);const n=t.children;for(let t=0;t<n.length;t++)e(n[t])}(f),function e(t){const n=t.getAttribute&&t.getAttribute("xlink:href"),i=t.getAttribute&&t.getAttribute("href");for(const e of[n,i])if(e&&e.startsWith("#")){const t=e.substring(1);let n=h.getElementById(t);if(!n){const e=document.getElementById(t);if(e){let t=h.querySelector("defs");t||(t=document.createElementNS("http://www.w3.org/2000/svg","defs"),h.insertBefore(t,h.firstChild)),n=e.cloneNode(!0),t.appendChild(n),p.add(t)}}if(n&&!p.has(n)){p.add(n);let e=n.parentNode;for(;e&&e!==h;)p.add(e),e=e.parentNode}}const o=t.getAttribute&&t.getAttribute("style");if(o){const e=o.match(/url\(#([^)]+)\)/g);if(e)for(const t of e){const e=t.match(/url\(#([^)]+)\)/)[1],n=h.getElementById(e);if(n&&!p.has(n)){p.add(n);let e=n.parentNode;for(;e&&e!==h;)p.add(e),e=e.parentNode}}}const s=t.children;for(let t=0;t<s.length;t++)e(s[t])}(f);const b=f.tagName&&f.tagName.toLowerCase();if("tspan"===b||"textpath"===b){const e=f.parentNode;if(e){const t=Array.from(e.children);for(const e of t){const t=e.tagName&&e.tagName.toLowerCase();"tspan"!==t&&"textpath"!==t||(p.add(e),function e(t){p.add(t);const n=t.children;for(let t=0;t<n.length;t++)e(n[t])}(e))}}}!function e(t){const n=Array.prototype.slice.call(t.children);for(let t=0;t<n.length;t++){const i=n[t];"defs"!==(i.tagName&&i.tagName.toLowerCase())&&(p.has(i)||i.contains(f)?e(i):i.parentNode.removeChild(i))}}(h);const v=(new XMLSerializer).serializeToString(h);const E=new Blob([v],{type:"image/svg+xml;charset=utf-8"}),S=URL.createObjectURL(E),$=new Image;$.decoding="async",$.crossOrigin="anonymous",$.src=S,await new Promise((s,r)=>{$.onload=function(){s()},$.onerror=function(s){const a=s&&"object"==typeof s&&"message"in s?String(s.message):"unknown error",l=t(e),d=i(e),h=n(e,o);r(new Error(`āŒ Failed to render SVG as image: ${a}\n\nELEMENT DETAILS:\n ${l}\n Font-family: ${d}\n SVG Root: ${o.id?`id="${o.id}"`:"(no id)"}\n`+h+"\nThis can happen when:\n 1. The SVG contains invalid XML syntax\n 2. Referenced resources (images, fonts) failed to load\n 3. The SVG uses unsupported features\n 4. Browser security policies blocked the rendering\n\nHow to fix:\n • Validate your SVG with an XML validator\n • Check that all external resources (images, fonts) are accessible\n • Ensure referenced elements (gradients, patterns, etc.) exist in <defs>\n • If fonts are missing, ensure they are installed or embedded in the SVG\n • Try simplifying the SVG to isolate the problematic element"))}});const V=document.createElement("canvas");V.width=y,V.height=w;const M=V.getContext("2d");let A;M.clearRect(0,0,y,w),M.drawImage($,0,0,y,w),URL.revokeObjectURL(S);try{A=M.getImageData(0,0,y,w)}catch(s){const r=s&&s.message&&/out of memory/i.test(s.message),l=s&&s.message&&/tainted/i.test(s.message);if(r){const i=t(e),r=n(e,o);throw new Error(`āŒ Cannot render SVG: Canvas out of memory\n\nELEMENT DETAILS:\n ${i}\n SVG Root: ${o.id?`id="${o.id}"`:"(no id)"}\n`+r+"\nThe SVG coordinates or dimensions are too large for canvas rasterization.\n"+`Current viewBox: ${a.x} ${a.y} ${a.width} ${a.height}\n`+`Attempted canvas size: ${y}Ɨ${w} pixels\n\nHow to fix:\n • Reduce the viewBox dimensions to a reasonable size (< 10,000 units)\n • Use smaller coordinates (avoid values > 100,000)\n • Decrease the pixelsPerUnit scaling factor\n • Split large SVG into smaller regions and process separately\n\n`+`Original error: ${s.message}`)}if(l){const r=t(e),a=i(e),l=n(e,o);throw new Error(`āŒ Cannot read SVG pixels: Canvas is tainted by cross-origin resources\n\nELEMENT DETAILS:\n ${r}\n Font-family: ${a}\n SVG Root: ${o.id?`id="${o.id}"`:"(no id)"}\n`+l+"\nThis happens when your SVG references external resources without CORS:\n • External images (PNG, JPG, etc.) from different domains\n • Web fonts from CDNs without proper CORS headers\n • SVG <use> elements referencing external files\n\nHow to fix:\n • Host images/fonts on the same domain as your page\n • Configure CORS headers on external resources (Access-Control-Allow-Origin: *)\n • Use data URLs for images instead of external URLs\n • Embed fonts directly in the SVG using @font-face with data URLs\n"+` • If using web fonts (like "${a}"), ensure they have CORS headers or embed them\n\n`+`Original error: ${s.message}`)}const d=t(e),h=i(e),c=n(e,o);throw new Error(`āŒ Cannot read SVG pixels from canvas\n\nELEMENT DETAILS:\n ${d}\n Font-family: ${h}\n SVG Root: ${o.id?`id="${o.id}"`:"(no id)"}\n`+c+"\n"+`Error: ${s&&s.message?s.message:"unknown canvas error"}\n\nCommon causes:\n • Cross-origin images/fonts without CORS (canvas becomes "tainted")\n • Canvas size too large (out of memory)\n • Browser security restrictions\n\nHow to fix:\n • Check browser console for specific CORS errors\n • Ensure all external resources are same-origin or have CORS enabled\n • Try reducing the SVG size or complexity`)}const B=A.data;let G=y,C=-1,T=w,R=-1;for(let e=0;e<w;e++){const t=e*y*4;for(let n=0;n<y;n++){0!==B[t+4*n+3]&&(n<G&&(G=n),n>C&&(C=n),e<T&&(T=e),e>R&&(R=e))}}if(C<G||R<T)return null;const L=a.x+G/m;return{x:L,y:a.y+T/m,width:(C-G+1)/m,height:(R-T+1)/m}}async function getSvgElementVisualBBoxTwoPassAggressive(n,i){const r=(i=i||{}).mode||"clipped",a="number"==typeof i.coarseFactor?i.coarseFactor:3,l="number"==typeof i.fineFactor?i.fineFactor:24,d="number"==typeof i.safetyMarginUser?i.safetyMarginUser:null,h="boolean"!=typeof i.useLayoutScale||i.useLayoutScale,c="number"==typeof i.fontTimeoutMs?i.fontTimeoutMs:8e3,u="string"==typeof n?document.getElementById(n):n;if(!u){throw new Error(`āŒ Cannot compute SVG bounding box: Element not found\n\nREQUESTED ELEMENT:\n ${"string"==typeof n?`id="${n}"`:"provided reference"}\n Type: ${typeof n}\n\nHow to fix:\n • Check that the element exists in the DOM\n • Verify the element ID is correct (case-sensitive)\n • Ensure the element hasn't been removed from the DOM\n • If passing an element reference, make sure it's not null/undefined`)}const g=u.ownerDocument||document;await waitForDocumentFonts(g,c);const f=u.ownerSVGElement||(u instanceof SVGSVGElement?u:null);if(!f){const n=t(u),i=u&&u.id&&e(u.id)?`\nāš ļø NOTE: This element has an AUTO-GENERATED ID ("${u.id}").\n This ID was added by sbb-extractor.cjs and doesn't exist in your original SVG.\n\n`:"";throw new Error(`āŒ Cannot compute SVG bounding box: Element is not inside an SVG tree\n\nELEMENT DETAILS:\n ${n}\n`+i+"This element is not connected to an <svg> root element.\n\nHow to fix:\n • Ensure the element is inside an <svg> tag in the DOM\n • Check that you're not querying a detached/orphaned element\n • If creating elements programmatically, append them to the SVG tree first\n • Verify the element hasn't been removed from the document")}const m=f.viewBox&&f.viewBox.baseVal;let y;if(m&&m.width&&m.height)y={x:m.x,y:m.y,width:m.width,height:m.height};else{const e=function(e){try{const t=e.getBoundingClientRect(),n=e.ownerSVGElement||(e instanceof SVGSVGElement?e:null);if(!n||!t)return null;if(0===t.width&&0===t.height)return null;const i=n.getBoundingClientRect(),s=n.viewBox&&n.viewBox.baseVal;if(!s||s.width<=0||s.height<=0)return null;if(i.width<=0||i.height<=0)return null;const r=o(n,s,i);let a,l,d,h;if(r.uniform){const e=r.scale;a=s.x+(t.left-i.left-r.offsetX)*e,l=s.y+(t.top-i.top-r.offsetY)*e,d=t.width*e,h=t.height*e}else a=s.x+(t.left-i.left)*r.scaleX,l=s.y+(t.top-i.top)*r.scaleY,d=t.width*r.scaleX,h=t.height*r.scaleY;return isFinite(a)&&isFinite(l)&&isFinite(d)&&isFinite(h)?{x:a,y:l,width:d,height:h}:null}catch{return null}}(f);y=e?{x:e.x,y:e.y,width:e.width,height:e.height}:{x:-2e3,y:-2e3,width:4e3,height:4e3}}const w=[];let p;if(function e(t){const n=t.getAttribute&&t.getAttribute("xlink:href"),i=t.getAttribute&&t.getAttribute("href");for(const e of[n,i])if(e&&e.startsWith("#")){const t=e.substring(1),n=f.getElementById(t);n&&!w.includes(n)&&w.push(n)}const o=t.getAttribute&&t.getAttribute("style");if(o){const e=o.match(/url\(#([^)]+)\)/g);if(e)for(const t of e){const e=t.match(/url\(#([^)]+)\)/)[1],n=f.getElementById(e);n&&!w.includes(n)&&w.push(n)}}const s=t.children;for(let t=0;t<s.length;t++)e(s[t])}(u),"unclipped"===r){let e=y.x,t=y.y,n=y.x+y.width,i=y.y+y.height;const s=o(f,y,f.getBoundingClientRect()),r=o=>{try{const r=o.getBoundingClientRect(),a=f.getBoundingClientRect();if(r.width>0&&r.height>0){let o,l,d,h;if(s.uniform){const e=s.scale;o=y.x+(r.left-a.left-s.offsetX)*e,l=y.y+(r.top-a.top-s.offsetY)*e,d=y.x+(r.right-a.left-s.offsetX)*e,h=y.y+(r.bottom-a.top-s.offsetY)*e}else o=y.x+(r.left-a.left)*s.scaleX,l=y.y+(r.top-a.top)*s.scaleY,d=y.x+(r.right-a.left)*s.scaleX,h=y.y+(r.bottom-a.top)*s.scaleY;const c=200;e=Math.min(e,o-c),t=Math.min(t,l-c),n=Math.max(n,d+c),i=Math.max(i,h+c)}}catch{}};r(u);for(const e of w)r(e);p={x:e,y:t,width:n-e,height:i-t}}else p={x:y.x,y:y.y,width:y.width,height:y.height};let x=1;if(h&&y.width>0&&y.height>0){const e=f.getBoundingClientRect();if(e&&e.width>0&&e.height>0){x=(e.width/y.width+e.height/y.height)/2}}const b=Math.max(1,x*a),v=Math.max(4,x*l);const E=await s(u,f,p,b);if(!E)return null;let S=d;if(null==S||!isFinite(S)){const e=Math.max(E.width,E.height);S=100+(e>0?.25*e:0)}const $=E.x-S,V=E.y-S,M=E.x+E.width+S,A=E.y+E.height+S,B={x:$,y:V,width:Math.max(0,M-$),height:Math.max(0,A-V)};if(B.width<=0||B.height<=0)return null;const G=await s(u,f,B,v);return G?{x:G.x,y:G.y,width:G.width,height:G.height,element:u,svgRoot:f}:null}async function getSvgElementsUnionVisualBBox(e,t){if(!Array.isArray(e)||0===e.length)throw new Error(`Cannot compute union bounding box: Invalid targets parameter\n\nExpected: Non-empty array of SVG elements or element IDs\nReceived: ${Array.isArray(e)?"empty array":typeof e}\n\nHow to fix:\n • Pass an array with at least one element\n • Example: ['element1', 'element2'] or [el1, el2]\n • Ensure the array is not empty before calling this function`);const n=[];let i=null;for(let o=0;o<e.length;o++){const s=e[o],r=await getSvgElementVisualBBoxTwoPassAggressive(s,t);if(r){if(i){if(r.svgRoot!==i){const e=i.id?`id="${i.id}"`:"(no id)",t=r.svgRoot.id?`id="${r.svgRoot.id}"`:"(no id)";throw new Error(`Cannot compute union bounding box: Elements from different SVG documents\n\nAll elements must belong to the same <svg> root element.\nPrevious SVG: ${e}\nCurrent SVG: ${t}\n\nHow to fix:\n • Ensure all elements are children of the same <svg> root\n • If you have multiple SVGs, compute bbox for each separately\n • Check that elements haven't been moved between different SVG trees`)}}else i=r.svgRoot;n.push(r)}}if(0===n.length)return null;let o=1/0,s=1/0,r=-1/0,a=-1/0;for(let e=0;e<n.length;e++){const t=n[e];o=Math.min(o,t.x),s=Math.min(s,t.y),r=Math.max(r,t.x+t.width),a=Math.max(a,t.y+t.height)}return{x:o,y:s,width:r-o,height:a-s,svgRoot:i,bboxes:n}}async function getSvgElementVisibleAndFullBBoxes(e,t){t=t||{};const n=Object.assign({},t,{mode:"clipped"}),i=Object.assign({},t,{mode:"unclipped"});return{visible:await getSvgElementVisualBBoxTwoPassAggressive(e,n),full:await getSvgElementVisualBBoxTwoPassAggressive(e,i)}}return{waitForDocumentFonts:waitForDocumentFonts,getSvgElementVisualBBoxTwoPassAggressive:getSvgElementVisualBBoxTwoPassAggressive,getSvgElementsUnionVisualBBox:getSvgElementsUnionVisualBBox,getSvgElementVisibleAndFullBBoxes:getSvgElementVisibleAndFullBBoxes,getSvgRootViewBoxExpansionForFullDrawing:async function(e,t){t=t||{};const n="string"==typeof e?document.getElementById(e):e;if(!(n&&n instanceof SVGSVGElement)){throw new Error(`Cannot compute viewBox expansion: Invalid SVG root (${"string"==typeof e?`id="${e}"`:"provided reference"})\n\nExpected: Root <svg> element or its ID\nReceived: ${n?n.tagName:"null/undefined"}\n\nHow to fix:\n • Pass the root <svg> element or its ID string\n • Ensure the element is actually an <svg> tag, not a child element\n • Check that the element exists in the DOM`)}const i=n.viewBox&&n.viewBox.baseVal;if(!i||!i.width||!i.height){const e=n.id?`id="${n.id}"`:"(no id)";throw new Error(`Cannot compute viewBox expansion: SVG missing valid viewBox (${e})\n\nThe root <svg> element must have a viewBox attribute with valid dimensions.\nCurrent viewBox: ${n.getAttribute("viewBox")||"(none)"}\n\nHow to fix:\n • Add a viewBox attribute to your <svg> tag\n • Example: <svg viewBox="0 0 800 600" ...>\n • Ensure viewBox has 4 numbers: x, y, width, height\n • Width and height must be greater than 0\n • Use the sbb-fix-viewbox.cjs tool to auto-generate viewBox`)}const o={x:i.x,y:i.y,width:i.width,height:i.height},s=await getSvgElementVisibleAndFullBBoxes(n,t),r=s.visible,a=s.full;if(!a)return null;const l=o.x+o.width,d=o.y+o.height,h=a.x+a.width,c=a.y+a.height,u=Math.max(0,o.x-a.x),g=Math.max(0,o.y-a.y),f=Math.max(0,h-l),m=Math.max(0,c-d);return{currentViewBox:o,visibleBBox:r,fullBBox:a,padding:{left:u,top:g,right:f,bottom:m},newViewBox:{x:o.x-u,y:o.y-g,width:o.width+u+f,height:o.height+g+m}}},showTrueBBoxBorder:async function(e,t){t=t||{};let n=null;if("string"==typeof e){if(n=document.querySelector(e),!n)throw new Error(`SVG element not found: ${e}`)}else{if(!(e instanceof Element))throw new Error("Target must be a selector string or DOM element");n=e}if("object"===n.tagName.toLowerCase()&&"contentDocument"in n&&n.contentDocument){const e=n.contentDocument.querySelector("svg");e&&(n=e)}else if("iframe"===n.tagName.toLowerCase()&&"contentDocument"in n&&n.contentDocument){const e=n.contentDocument.querySelector("svg");e&&(n=e)}else n.tagName.toLowerCase();if(!n||!("ownerSVGElement"in n)&&"svg"!==n.tagName.toLowerCase())throw new Error("Target is not an SVG element or does not contain SVG content");const i=t.theme||"auto";let o=!1;"auto"===i?o=window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches:"dark"===i?o=!0:"light"===i&&(o=!1);const s=o?"rgba(255,255,255,0.8)":"rgba(0,0,0,0.6)",r=t.borderColor||s,a=t.borderWidth||"2px",l=t.borderStyle||"dashed",d=void 0!==t.padding?t.padding:4,h=void 0!==t.zIndex?t.zIndex:999999,c=t.bboxOptions||{},u=await getSvgElementVisualBBoxTwoPassAggressive(n,c);if(!u||0===u.width||0===u.height)return console.warn("showTrueBBoxBorder: Element has zero-size bounding box",u),{bbox:u,overlay:null,remove:()=>{}};const g=("ownerSVGElement"in n?n.ownerSVGElement:null)||n,f=g.getBoundingClientRect(),m=g.viewBox.baseVal;let y=0,w=0,p=f.width,x=f.height;m&&m.width>0&&m.height>0&&(y=m.x,w=m.y,p=m.width,x=m.height);const b=f.width/p,v=f.height/x,E=f.left+(u.x-y)*b-d,S=f.top+(u.y-w)*v-d,$=u.width*b+2*d,V=u.height*v+2*d,M=document.createElement("div");M.style.position="fixed",M.style.left=`${E}px`,M.style.top=`${S}px`,M.style.width=`${$}px`,M.style.height=`${V}px`,M.style.border=`${a} ${l} ${r}`,M.style.pointerEvents="none",M.style.zIndex=String(h),M.style.boxSizing="border-box",M.setAttribute("data-svg-bbox-overlay","true"),M.setAttribute("data-target-id",n.id||"anonymous"),document.body.appendChild(M);const A=()=>{const e=g.getBoundingClientRect(),t=e.width/p,n=e.height/x,i=e.left+(u.x-y)*t-d,o=e.top+(u.y-w)*n-d,s=u.width*t+2*d,r=u.height*n+2*d;M.style.left=`${i}px`,M.style.top=`${o}px`,M.style.width=`${s}px`,M.style.height=`${r}px`},B=()=>A(),G=()=>A();return window.addEventListener("scroll",B,!0),window.addEventListener("resize",G),{bbox:u,overlay:M,remove:()=>{M.parentNode&&M.parentNode.removeChild(M),window.removeEventListener("scroll",B,!0),window.removeEventListener("resize",G)}}},setViewBoxOnObjects:async function(e,t,n){const i={aspect:(n=n||{}).aspect||"stretch",aspectRatioMode:n.aspectRatioMode||"meet",align:n.align||"xMidYMid",visibility:n.visibility||"unchanged",visibilityList:n.visibilityList||null,margin:n.margin||0,saveVisibilityList:n.saveVisibilityList||!1,dryRun:n.dryRun||!1,bboxOptions:n.bboxOptions||{}};let o;if("string"==typeof e){const t=e.startsWith("#")?e:"#"+e;o=document.querySelector(t),o||(o=document.getElementById(e))}else o=e;if(!o||"svg"!==o.tagName.toLowerCase())throw new Error("Target must be an SVG element");const s=Array.isArray(t)?t:[t];if(0===s.length)throw new Error("At least one object ID must be provided");const r=s.map(e=>{let t=document.getElementById(e);if(!t)throw new Error(`Element with ID "${e}" not found`);if("symbol"===t.tagName.toLowerCase()){const n=Array.from(o.querySelectorAll("use")).filter(t=>(t.getAttribute("href")||t.getAttributeNS("http://www.w3.org/1999/xlink","href"))==="#"+e);if(0===n.length)throw new Error(`Symbol "${e}" has no <use> elements referencing it`);if(n.length>1)throw new Error(`Symbol "${e}" is referenced by multiple <use> elements. Please specify the exact <use> element ID.`);t=n[0]}return t});let a;if(await waitForDocumentFonts(document,i.bboxOptions.fontTimeoutMs||8e3),a=1===r.length?await getSvgElementVisualBBoxTwoPassAggressive(r[0],i.bboxOptions):await getSvgElementsUnionVisualBBox(r,i.bboxOptions),!a||a.width<=0||a.height<=0)throw new Error("Could not compute valid bounding box for specified objects");const l=o.viewBox.baseVal,d={x:l.x||0,y:l.y||0,width:l.width||parseFloat(o.getAttribute("width")||"0")||0,height:l.height||parseFloat(o.getAttribute("height")||"0")||0};if(0===d.width||0===d.height){const e=o.getBoundingClientRect();d.width=e.width,d.height=e.height}let h,c=0;if("string"==typeof i.margin){const e=i.margin.match(/^([\d.]+)(px|%)?$/);if(e){const t=parseFloat(e[1]),n=e[2];if("px"===n){c=t/(o.getBoundingClientRect().width/d.width)}else if("%"===n){c=t/100*Math.sqrt(a.width*a.width+a.height*a.height)}else c=t}}else c=i.margin;if("stretch"===i.aspect)h={x:a.x-c,y:a.y-c,width:a.width+2*c,height:a.height+2*c};else if("changePosition"===i.aspect){const e=a.x+a.width/2,t=a.y+a.height/2;h={x:e-d.width/2,y:t-d.height/2,width:d.width,height:d.height}}else{if("preserveAspectRatio"!==i.aspect)throw new Error(`Unknown aspect mode: ${i.aspect}`);{const e=d.width/d.height,t=(a.width+2*c)/(a.height+2*c);let n,o;"meet"===i.aspectRatioMode?e>t?(o=a.height+2*c,n=o*e):(n=a.width+2*c,o=n/e):e>t?(n=a.width+2*c,o=n/e):(o=a.height+2*c,n=o*e);const s=a.x+a.width/2,r=a.y+a.height/2,l=i.align.match(/^x(Min|Mid|Max)Y(Min|Mid|Max)$/),u=l?l[1]:"Mid",g=l?l[2]:"Mid";let f,m;f="Min"===u?a.x-c:"Max"===u?a.x+a.width+c-n:s-n/2,m="Min"===g?a.y-c:"Max"===g?a.y+a.height+c-o:r-o/2,h={x:f,y:m,width:n,height:o}}}let u=null;if(i.saveVisibilityList){u={};o.querySelectorAll("[id]").forEach(e=>{if(e instanceof HTMLElement||e instanceof SVGElement){const t=window.getComputedStyle(e);u[e.id]={display:e.style.display||t.display,visibility:e.style.visibility||t.visibility,opacity:e.style.opacity||t.opacity}}})}const g={};if("unchanged"!==i.visibility&&!i.dryRun)if("hideAllExcept"===i.visibility){o.querySelectorAll("[id]").forEach(e=>{(e instanceof HTMLElement||e instanceof SVGElement)&&(g[e.id]=e.style.display,s.includes(e.id)||(e.style.display="none"))})}else"hideTargets"===i.visibility?r.forEach(e=>{(e instanceof HTMLElement||"undefined"!=typeof SVGElement&&e instanceof SVGElement)&&(g[e.id]=e.style.display,e.style.display="none")}):"restoreList"===i.visibility&&i.visibilityList&&Object.keys(i.visibilityList).forEach(e=>{const t=document.getElementById(e);if(t){const n=i.visibilityList[e];g[e]={display:t.style.display,visibility:t.style.visibility,opacity:t.style.opacity},void 0!==n.display&&(t.style.display=n.display),void 0!==n.visibility&&(t.style.visibility=n.visibility),void 0!==n.opacity&&(t.style.opacity=n.opacity)}});const f=`${d.x} ${d.y} ${d.width} ${d.height}`;return i.dryRun||o.setAttribute("viewBox",`${h.x} ${h.y} ${h.width} ${h.height}`),{newViewBox:h,oldViewBox:d,bbox:a,visibilityList:u,restore:()=>{o.setAttribute("viewBox",f),Object.keys(g).forEach(e=>{const t=document.getElementById(e);if(t){const n=g[e];"string"==typeof n?t.style.display=n:(void 0!==n.display&&(t.style.display=n.display),void 0!==n.visibility&&(t.style.visibility=n.visibility),void 0!==n.opacity&&(t.style.opacity=n.opacity))}})}}}}});