@visactor/vdataset
Version: 
data processing tool
158 lines (147 loc) • 8.48 kB
JavaScript
import { Matrix, isString, isValid, isValidNumber, merge, toCamelCase } from "@visactor/vutils";
const tagNameToType = {
    svg: "group",
    rect: "rect",
    line: "rule",
    polygon: "polygon",
    path: "path",
    polyline: "line",
    g: "group",
    circle: "arc",
    ellipse: "arc"
}, validTagName = Object.keys(tagNameToType), validGroupNode = [ "g", "svg", "text", "tspan", "switch" ], validTextAttributes = [ "font-size", "font-family", "font-weight", "font-style", "text-align", "text-anchor" ], validCircleAttributes = [ "cx", "cy", "r" ], validEllipseAttributes = [ "cx", "cy", "rx", "ry" ], validLineAttributes = [ "x1", "x2", "y1", "y2" ], validAttributes = [ "visibility", "x", "y", "width", "height", "d", "points", "stroke", "stroke-width", "fill", "fill-opacity", "stroke-opacity", ...validTextAttributes, ...validCircleAttributes, ...validEllipseAttributes, ...validLineAttributes ], validInheritAttributes = [ "visible", "fill", "stroke", "stroke-width", "fill-opacity", "stroke-opacity", ...validTextAttributes ], numberReg = /-?([0-9]*\.)?[0-9]+([eE]-?[0-9]+)?/g;
function splitNumberSequence(rawStr) {
    return rawStr.match(numberReg) || [];
}
export const svgParser = (data, option = {}, dataView) => {
    let parser = option.customDOMParser;
    if (parser || (null === window || void 0 === window ? void 0 : window.DOMParser) && (parser = svg => (new DOMParser).parseFromString(svg, "text/xml")), 
    !parser) throw new Error("No Available DOMParser!");
    const svg = parser(data);
    let node = 9 === svg.nodeType ? svg.firstChild : svg;
    for (;node && ("svg" !== node.nodeName.toLowerCase() || 1 !== node.nodeType); ) node = node.nextSibling;
    if (node) {
        return parseSvgNode(node);
    }
    return null;
};
let idx = 0;
function parseSvgNode(svg, opt = {}) {
    const elements = [], root = parseNode(svg, null);
    let width = parseFloat(svg.getAttribute("width") || opt.width), height = parseFloat(svg.getAttribute("height") || opt.height);
    !isValidNumber(width) && (width = null), !isValidNumber(height) && (height = null);
    const viewBox = svg.getAttribute("viewBox");
    let viewBoxRect;
    if (viewBox) {
        const viewBoxArr = splitNumberSequence(viewBox);
        if (viewBoxArr.length >= 4 && (viewBoxRect = {
            x: parseFloat(viewBoxArr[0] || 0),
            y: parseFloat(viewBoxArr[1] || 0),
            width: parseFloat(viewBoxArr[2]),
            height: parseFloat(viewBoxArr[3])
        }, width || height)) {
            const boundingRect = {
                x: 0,
                y: 0,
                width: width,
                height: height
            }, scaleX = boundingRect.width / viewBoxRect.width, scaleY = boundingRect.height / viewBoxRect.height, scale = Math.min(scaleX, scaleY), transLateX = -(viewBoxRect.x + viewBoxRect.width / 2) * scale + (boundingRect.x + boundingRect.width / 2), transLateY = -(viewBoxRect.y + viewBoxRect.height / 2) * scale + (boundingRect.y + boundingRect.height / 2), viewBoxTransform = (new Matrix).translate(transLateX, transLateY).scale(scale, scale);
            root.transform = viewBoxTransform;
        }
    }
    return traverse(svg, root, elements), {
        root: root,
        width: width,
        height: height,
        elements: elements,
        viewBoxRect: viewBoxRect
    };
}
function parseInheritAttributes(parsedElement) {
    let inheritedAttrs;
    const {parent: parent, attributes: attributes} = parsedElement, parse = parent => parent ? validInheritAttributes.reduce(((acc, attrName) => {
        const camelAttrName = toCamelCase(attrName);
        return isValid(parent[camelAttrName]) && (acc[camelAttrName] = parent[camelAttrName]), 
        acc;
    }), {}) : {};
    return parent ? (parent._inheritStyle || (parent._inheritStyle = parse(parent.attributes)), 
    inheritedAttrs = merge({}, parent._inheritStyle, parse(attributes))) : inheritedAttrs = parse(attributes), 
    inheritedAttrs;
}
function parseAttributes(el) {
    var _a, _b, _c;
    const attrs = {}, attributes = null !== (_a = el.attributes) && void 0 !== _a ? _a : {}, style = null !== (_b = el.style) && void 0 !== _b ? _b : {};
    for (let i = 0; i < validAttributes.length; i++) {
        const attrName = validAttributes[i], attrValue = isValid(style[attrName]) && "" !== style[attrName] ? style[attrName] : null === (_c = attributes[attrName]) || void 0 === _c ? void 0 : _c.value;
        isValid(attrValue) && (attrs[toCamelCase(attrName)] = isNaN(+attrValue) ? attrValue : parseFloat(attrValue));
    }
    return "none" === style.display && (attrs.visible = !1), [ "fontSize", "strokeWidth", "width", "height" ].forEach((attr => {
        const attrValue = attrs[attr];
        isString(attrs[attr]) && (attrs[attr] = parseFloat(attrValue));
    })), attrs;
}
function parseNode(node, parent) {
    var _a, _b, _c, _d, _e;
    const tagName = null === (_a = node.tagName) || void 0 === _a ? void 0 : _a.toLowerCase();
    if (3 === node.nodeType || "text" === tagName || "tspan" === tagName) return parseText(node, parent);
    if (!validTagName.includes(tagName)) return null;
    const parsed = {
        tagName: tagName,
        graphicType: tagNameToType[tagName],
        attributes: parseAttributes(node),
        parent: parent,
        name: null !== (_b = node.getAttribute("name")) && void 0 !== _b ? _b : null === (_c = null == parent ? void 0 : parent.attributes) || void 0 === _c ? void 0 : _c.name,
        id: null !== (_d = node.getAttribute("id")) && void 0 !== _d ? _d : `${tagName}-${idx++}`,
        transform: parseTransform(node)
    };
    return parsed._inheritStyle = parseInheritAttributes(parsed), parent && !isValid(parsed.name) && (parsed._nameFromParent = null !== (_e = parent.name) && void 0 !== _e ? _e : parent._nameFromParent), 
    parsed;
}
function parseText(node, parent) {
    var _a, _b, _c, _d, _e, _f;
    if (!parent) return null;
    const tagName = null === (_a = node.tagName) || void 0 === _a ? void 0 : _a.toLowerCase();
    if (!tagName && "group" !== parent.graphicType) return null;
    const nodeAsGroup = "text" === tagName || "tspan" === tagName, elType = nodeAsGroup ? "group" : "text", value = nodeAsGroup || null === (_b = node.textContent) || void 0 === _b ? void 0 : _b.replace(/\n/g, " ").replace(/\s+/g, " ");
    if (" " === value) return null;
    let parsed;
    return parsed = nodeAsGroup ? {
        tagName: tagName,
        graphicType: elType,
        attributes: parseAttributes(node),
        parent: parent,
        name: node.getAttribute("name"),
        id: null !== (_c = node.getAttribute("id")) && void 0 !== _c ? _c : `${tagName}-${idx++}`,
        transform: parseTransform(node),
        value: value
    } : {
        tagName: tagName,
        graphicType: "text",
        attributes: parseAttributes(node),
        parent: parent,
        name: null == parent ? void 0 : parent.name,
        id: null !== (_e = null === (_d = node.getAttribute) || void 0 === _d ? void 0 : _d.call(node, "id")) && void 0 !== _e ? _e : `${tagName}-${idx++}`,
        value: value
    }, parsed._inheritStyle = parseInheritAttributes(parsed), isValid(parsed.name) || (parsed._nameFromParent = null !== (_f = parent.name) && void 0 !== _f ? _f : parent._nameFromParent), 
    nodeAsGroup ? parent._textGroupStyle ? parsed._textGroupStyle = merge({}, parent._textGroupStyle, parseAttributes(node)) : parsed._textGroupStyle = parseAttributes(node) : parsed.attributes = parsed._inheritStyle, 
    parsed;
}
function parseTransform(node) {
    var _a, _b;
    const transforms = null === (_a = node.transform) || void 0 === _a ? void 0 : _a.baseVal;
    if (!transforms) return null;
    const matrix = null === (_b = transforms.consolidate()) || void 0 === _b ? void 0 : _b.matrix;
    if (!matrix) return null;
    const {a: a, b: b, c: c, d: d, e: e, f: f} = matrix;
    return new Matrix(a, b, c, d, e, f);
}
function traverse(node, parsedParent, result = []) {
    var _a;
    if (!node) return;
    let parseResult;
    "svg" !== node.nodeName && (parseResult = parseNode(node, parsedParent)), parseResult && result.push(parseResult);
    let child = validGroupNode.includes(null === (_a = node.tagName) || void 0 === _a ? void 0 : _a.toLocaleLowerCase()) ? node.firstChild : null;
    for (;child; ) traverse(child, null != parseResult ? parseResult : parsedParent, result), 
    child = child.nextSibling;
}
//# sourceMappingURL=svg.js.map