1 export class SvgDumper {
3 private containerElements = ["svg", "g"];
4 private embeddableStyles = {
5 "rect": ["fill", "stroke", "stroke-width"],
6 "path": ["fill", "stroke", "stroke-width"],
7 "circle": ["fill", "stroke", "stroke-width"],
8 "line": ["stroke", "stroke-width"],
9 "text": ["fill", "font-size", "text-anchor", "font-family"],
10 "polygon": ["stroke", "fill"]
13 constructor(private svg: SVGSVGElement) {
17 dump({padding} = {padding: 50}): string {
18 this.adaptViewbox(this.svg, padding);
19 const clone = this.svg.cloneNode(true) as SVGSVGElement;
21 const portLabels: any = clone.querySelectorAll(".port .label");
24 for (const label of portLabels) {
25 label.parentNode.removeChild(label);
28 this.treeShakeStyles(clone, this.svg);
30 // Remove panning handle so we don't have to align it
31 const panHandle = clone.querySelector(".pan-handle");
33 clone.removeChild(panHandle);
36 return new XMLSerializer().serializeToString(clone);
40 private adaptViewbox(svg: SVGSVGElement, padding = 50) {
41 const workflow = svg.querySelector(".workflow");
42 const rect = workflow!.getBoundingClientRect();
44 const origin = this.getPointOnSVG(rect.left, rect.top);
46 const viewBox = this.svg.viewBox.baseVal;
47 viewBox.x = origin.x - padding / 2;
48 viewBox.y = origin.y - padding / 2;
49 viewBox.height = rect.height + padding;
50 viewBox.width = rect.width + padding;
54 private getPointOnSVG(x: number, y: number): SVGPoint {
55 const svgCTM = this.svg.getScreenCTM();
56 const point = this.svg.createSVGPoint();
60 return point.matrixTransform(svgCTM!.inverse());
64 private treeShakeStyles(clone: SVGElement, original: SVGElement) {
66 const children = clone.childNodes;
67 const originalChildrenData = original.childNodes as NodeListOf<SVGElement>;
70 for (let childIndex = 0; childIndex < children.length; childIndex++) {
72 const child = children[childIndex] as SVGElement;
73 const tagName = child.tagName;
75 if (this.containerElements.indexOf(tagName) !== -1) {
76 this.treeShakeStyles(child, originalChildrenData[childIndex]);
77 } else if (tagName in this.embeddableStyles) {
79 const styleDefinition = window.getComputedStyle(originalChildrenData[childIndex]);
82 for (let st = 0; st < this.embeddableStyles[tagName].length; st++) {
84 this.embeddableStyles[tagName][st]
86 + styleDefinition.getPropertyValue(this.embeddableStyles[tagName][st])
90 child.setAttribute("style", styleString);