@thi.ng/hiccup-svg
Version:
SVG element functions for @thi.ng/hiccup & related tooling
207 lines (206 loc) • 5.33 kB
JavaScript
import { implementsFunction } from "@thi.ng/checks/implements-function";
import { isArray } from "@thi.ng/checks/is-array";
import { circle } from "./circle.js";
import { ellipse } from "./ellipse.js";
import { PRECISION, fattribs, setPrecision } from "./format.js";
import { linearGradient, radialGradient } from "./gradients.js";
import { __groupLayerID } from "./group.js";
import { image } from "./image.js";
import { hline, line, vline } from "./line.js";
import { path } from "./path.js";
import { packedPoints, points } from "./points.js";
import { polygon } from "./polygon.js";
import { polyline } from "./polyline.js";
import { roundedRect } from "./rect.js";
import { text } from "./text.js";
const ATTRIB_ALIASES = {
alpha: "opacity",
dash: "stroke-dasharray",
dashOffset: "stroke-dashoffset",
fillRule: "fill-rule",
lineCap: "stroke-linecap",
lineJoin: "stroke-linejoin",
miterLimit: "stroke-miterlimit",
weight: "stroke-width"
};
const TEXT_ALIGN = {
left: "start",
right: "end",
center: "middle",
start: "start",
end: "end"
};
const BASE_LINE = {
top: "text-top",
bottom: "text-bottom"
};
const precisionStack = [];
const convertTree = (tree) => {
if (tree == null) return null;
if (implementsFunction(tree, "toHiccup")) {
return convertTree(tree.toHiccup());
}
const type = tree[0];
if (isArray(type)) {
return tree.map(convertTree);
}
let attribs = __convertAttribs(tree[1]);
if (attribs.__convert === false) return tree;
if (attribs.__prec) {
precisionStack.push(PRECISION);
setPrecision(attribs.__prec);
}
let result;
switch (tree[0]) {
case "svg":
case "defs":
case "a":
case "g":
result = [type, fattribs(attribs)];
if (tree[0] === "g") __groupLayerID(result[1]);
for (let i = 2, n = tree.length; i < n; i++) {
const c = convertTree(tree[i]);
c != null && result.push(c);
}
break;
case "linearGradient":
result = linearGradient(
attribs.id,
attribs.from,
attribs.to,
tree[2],
{
gradientUnits: attribs.gradientUnits || "userSpaceOnUse",
...attribs.gradientTransform ? { gradientTransform: attribs.gradientTransform } : null
}
);
break;
case "radialGradient":
result = radialGradient(
attribs.id,
attribs.from,
attribs.to,
attribs.r1,
attribs.r2,
tree[2],
{
gradientUnits: attribs.gradientUnits || "userSpaceOnUse",
...attribs.gradientTransform ? { gradientTransform: attribs.gradientTransform } : null
}
);
break;
case "circle":
result = circle(tree[2], tree[3], attribs, ...tree.slice(4));
break;
case "ellipse":
result = ellipse(
tree[2],
tree[3][0],
tree[3][1],
attribs,
...tree.slice(4)
);
break;
case "rect": {
const r = tree[5] ?? attribs.r ?? 0;
result = roundedRect(
tree[2],
tree[3],
tree[4],
r,
r,
attribs,
...tree.slice(6)
);
break;
}
case "line":
result = line(tree[2], tree[3], attribs, ...tree.slice(4));
break;
case "hline":
result = hline(tree[2], attribs);
break;
case "vline":
result = vline(tree[2], attribs);
break;
case "polyline":
result = polyline(tree[2], attribs, ...tree.slice(3));
break;
case "polygon":
result = polygon(tree[2], attribs, ...tree.slice(3));
break;
case "path":
result = path(tree[2], attribs, ...tree.slice(3));
break;
case "text":
result = text(tree[2], tree[3], attribs, ...tree.slice(4));
break;
case "img":
result = image(tree[3], tree[2].src, attribs, ...tree.slice(4));
break;
case "points":
result = points(
tree[2],
attribs.shape,
attribs.size,
attribs,
...tree.slice(3)
);
break;
case "packedPoints":
result = packedPoints(
tree[2],
attribs.shape,
attribs.size,
attribs,
...tree.slice(3)
);
break;
default:
result = tree;
}
if (attribs.__prec) {
setPrecision(precisionStack.pop());
}
return result;
};
const __convertAttribs = (attribs) => {
const res = {};
if (!attribs) return res;
for (let id in attribs) {
const v = attribs[id];
const aid = ATTRIB_ALIASES[id];
if (aid) {
res[aid] = v;
} else {
__convertAttrib(res, id, v);
}
}
return res;
};
const __convertAttrib = (res, id, v) => {
switch (id) {
case "font": {
const i = v.indexOf(" ");
res["font-size"] = v.substring(0, i);
res["font-family"] = v.substring(i + 1);
break;
}
case "align":
res["text-anchor"] = TEXT_ALIGN[v];
break;
case "baseline":
res["dominant-baseline"] = BASE_LINE[v] || v;
break;
// case "filter":
// TODO needs to be translated into <filter> def first
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/filter
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element/filter
// break;
default:
res[id] = v;
}
};
export {
convertTree
};