export class SvgDumper { private svg : SVGSVGElement ; private containerElements = [ "svg" , "g" ] ; private embeddableStyles = { "rect" : [ "fill" , "stroke" , "stroke-width" ] , "path" : [ "fill" , "stroke" , "stroke-width" ] , "circle" : [ "fill" , "stroke" , "stroke-width" ] , "line" : [ "stroke" , "stroke-width" ] , "text" : [ "fill" , "font-size" , "text-anchor" , "font-family" ] , "polygon" : [ "stroke" , "fill" ] } ; constructor ( svg ) { this . svg = svg ; } dump ( { padding } = { padding : 50 } ) { this . adaptViewbox ( this . svg , padding ) ; const clone = this . svg . cloneNode ( true ) as SVGSVGElement ; const portLabels = clone . querySelectorAll ( ".port .label" ) ; for ( const label of portLabels ) { label . parentNode . removeChild ( label ) ; } this . treeShakeStyles ( clone , this . svg ) ; const panHandle = clone . querySelector ( ".pan-handle" ) ; if ( panHandle ) { clone . removeChild ( panHandle ) ; } return new XMLSerializer ( ) . serializeToString ( clone ) ; } private adaptViewbox ( svg , padding = 50 ) { const workflow = svg . querySelector ( ".workflow" ) ; const rect = workflow . getBoundingClientRect ( ) ; const origin = this . getPointOnSVG ( rect . left , rect . top ) ; const viewBox = this . svg . viewBox . baseVal ; viewBox . x = origin . x - padding / 2 ; viewBox . y = origin . y - padding / 2 ; viewBox . height = rect . height + padding ; viewBox . width = rect . width + padding ; } private getPointOnSVG ( x , y ) { const svgCTM = this . svg . getScreenCTM ( ) ; const point = this . svg . createSVGPoint ( ) ; point . x = x ; point . y = y ; return point . matrixTransform ( svgCTM . inverse ( ) ) ; } private treeShakeStyles ( clone , original ) { const children = clone . childNodes ; const originalChildrenData = original . childNodes as NodeListOf < SVGElement > ; for ( let childIndex = 0 ; childIndex < children . length ; childIndex ++ ) { const child = children [ childIndex ] as SVGElement ; const tagName = child . tagName ; if ( this . containerElements . indexOf ( tagName ) !== - 1 ) { this . treeShakeStyles ( child , originalChildrenData [ childIndex ] ) ; } else if ( tagName in this . embeddableStyles ) { const styleDefinition = window . getComputedStyle ( originalChildrenData [ childIndex ] ) ; let styleString = "" ; for ( let st = 0 ; st < this . embeddableStyles [ tagName ] . length ; st ++ ) { styleString += this . embeddableStyles [ tagName ] [ st ] + ":" + styleDefinition . getPropertyValue ( this . embeddableStyles [ tagName ] [ st ] ) + "; " ; } child . setAttribute ( "style" , styleString ) ; } } } }