fix svg loading to remove garbage icons
This commit is contained in:
@@ -1112,6 +1112,90 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseViewBox(svgEl) {
|
||||||
|
const raw = String(svgEl.getAttribute("viewBox") || "").trim();
|
||||||
|
const parts = raw.split(/\s+/).map((item) => Number(item));
|
||||||
|
if (parts.length !== 4 || parts.some((v) => Number.isNaN(v))) {
|
||||||
|
return { minX: 0, minY: 0, width: 100, height: 100 };
|
||||||
|
}
|
||||||
|
return { minX: parts[0], minY: parts[1], width: parts[2], height: parts[3] };
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupLooksOffCanvas(groupEl, viewBox) {
|
||||||
|
const attrNames = new Set(["x", "y", "cx", "cy", "x1", "y1", "x2", "y2", "d", "points"]);
|
||||||
|
const allElements = [groupEl, ...groupEl.querySelectorAll("*")];
|
||||||
|
let farOutsideCount = 0;
|
||||||
|
let numericCount = 0;
|
||||||
|
const minAllowedX = viewBox.minX - Math.max(40, viewBox.width * 0.8);
|
||||||
|
const minAllowedY = viewBox.minY - Math.max(40, viewBox.height * 0.8);
|
||||||
|
const maxAllowedX = viewBox.minX + viewBox.width + Math.max(40, viewBox.width * 0.8);
|
||||||
|
const maxAllowedY = viewBox.minY + viewBox.height + Math.max(40, viewBox.height * 0.8);
|
||||||
|
|
||||||
|
for (const node of allElements) {
|
||||||
|
for (const attr of node.getAttributeNames()) {
|
||||||
|
if (!attrNames.has(attr)) continue;
|
||||||
|
const value = node.getAttribute(attr);
|
||||||
|
if (!value) continue;
|
||||||
|
const matches = value.match(/-?\d*\.?\d+/g);
|
||||||
|
if (!matches) continue;
|
||||||
|
|
||||||
|
for (let idx = 0; idx < matches.length; idx += 1) {
|
||||||
|
const num = Number(matches[idx]);
|
||||||
|
if (Number.isNaN(num)) continue;
|
||||||
|
numericCount += 1;
|
||||||
|
const isXCoord = idx % 2 === 0;
|
||||||
|
if (isXCoord) {
|
||||||
|
if (num < minAllowedX || num > maxAllowedX) farOutsideCount += 1;
|
||||||
|
} else {
|
||||||
|
if (num < minAllowedY || num > maxAllowedY) farOutsideCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numericCount < 10) return false;
|
||||||
|
return farOutsideCount / numericCount > 0.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
function maxNestedGroupDepth(groupEl) {
|
||||||
|
let maxDepth = 1;
|
||||||
|
const stack = [{ node: groupEl, depth: 1 }];
|
||||||
|
while (stack.length > 0) {
|
||||||
|
const entry = stack.pop();
|
||||||
|
if (!entry) continue;
|
||||||
|
maxDepth = Math.max(maxDepth, entry.depth);
|
||||||
|
for (const child of Array.from(entry.node.children)) {
|
||||||
|
if (!child.tagName || child.tagName.toLowerCase() !== "g") continue;
|
||||||
|
stack.push({ node: child, depth: entry.depth + 1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeHeadSvgMarkup(svgMarkup) {
|
||||||
|
if (!svgMarkup) return null;
|
||||||
|
try {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const parsed = parser.parseFromString(svgMarkup, "image/svg+xml");
|
||||||
|
const svgEl = parsed.querySelector("svg");
|
||||||
|
if (!svgEl) return svgMarkup;
|
||||||
|
|
||||||
|
const topLevelGroups = Array.from(svgEl.children).filter((el) => el.tagName && el.tagName.toLowerCase() === "g");
|
||||||
|
if (topLevelGroups.length > 1) {
|
||||||
|
const viewBox = parseViewBox(svgEl);
|
||||||
|
const firstGroup = topLevelGroups[0];
|
||||||
|
const deeplyNestedGroup = maxNestedGroupDepth(firstGroup) >= 3;
|
||||||
|
if (groupLooksOffCanvas(firstGroup, viewBox) || deeplyNestedGroup) {
|
||||||
|
firstGroup.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new XMLSerializer().serializeToString(svgEl);
|
||||||
|
} catch {
|
||||||
|
return svgMarkup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function preloadReplaySvgs() {
|
async function preloadReplaySvgs() {
|
||||||
if (!replay || !Array.isArray(replay.turns)) return;
|
if (!replay || !Array.isArray(replay.turns)) return;
|
||||||
const urls = new Set();
|
const urls = new Set();
|
||||||
@@ -1135,7 +1219,7 @@
|
|||||||
if (type === "head") {
|
if (type === "head") {
|
||||||
const svgMarkup = svgCache.get(iconUrl);
|
const svgMarkup = svgCache.get(iconUrl);
|
||||||
if (svgMarkup) {
|
if (svgMarkup) {
|
||||||
layer.innerHTML = svgMarkup;
|
layer.innerHTML = normalizeHeadSvgMarkup(svgMarkup);
|
||||||
const svgEl = layer.querySelector("svg");
|
const svgEl = layer.querySelector("svg");
|
||||||
if (svgEl) {
|
if (svgEl) {
|
||||||
svgEl.style.width = "100%";
|
svgEl.style.width = "100%";
|
||||||
|
|||||||
Reference in New Issue
Block a user