import { applyClipPath } from './clipping' ; import { drawCircle } from './geom/circle' ; import { drawEllipse } from './geom/ellipse' ; import { drawImage } from './geom/image' ; import { drawLine } from './geom/line' ; import { drawPath } from './geom/path' ; import { drawPolygon } from './geom/polygon' ; import { drawPolyline } from './geom/polyline' ; import { drawRect } from './geom/rect' ; import { drawText } from './geom/text' ; import { drawUse } from './geom/use' ; import { RenderMode } from './RenderMode' ; import { getCombinedTransform , getNodeChildren , isHidden , RenderContext } from './utils' ; param root param width param height export function processRoot ( context , root , svgTransform , width ? , height ? ) { const stack = [ ] ; if ( root instanceof SVGSVGElement || root instanceof SVGSymbolElement || root instanceof SVGMarkerElement ) { let rootX = 0 ; let rootY = 0 ; if ( root instanceof SVGSymbolElement ) { rootX = parseFloat ( root . getAttribute ( 'x' ) ?? '' ) || 0 ; rootY = parseFloat ( root . getAttribute ( 'y' ) ?? '' ) || 0 ; width = width || parseFloat ( root . getAttribute ( 'width' ) ! ) || void 0 ; height = height || parseFloat ( root . getAttribute ( 'height' ) ! ) || void 0 ; } else if ( root instanceof SVGMarkerElement ) { rootX = - root . refX . baseVal . value ; rootY = - root . refY . baseVal . value ; width = width || parseFloat ( root . getAttribute ( 'markerWidth' ) ! ) || void 0 ; height = height || parseFloat ( root . getAttribute ( 'markerHeight' ) ! ) || void 0 ; } else { rootX = root . x . baseVal . value ; rootY = root . y . baseVal . value ; } let rootTransform = context . sourceSvg . createSVGMatrix ( ) ; if ( typeof width !== 'undefined' && typeof height !== 'undefined' && root . getAttribute ( 'viewBox' ) ) { const { x : viewBoxX , y : viewBoxY , width : viewBoxWidth , height : viewBoxHeight } = root . viewBox . baseVal ; if ( root . tagName === 'marker' ) { rootTransform = rootTransform . translate ( - viewBoxX * ( width ! / viewBoxWidth ) , - viewBoxY * ( height ! / viewBoxHeight ) ) . scaleNonUniform ( width ! / viewBoxWidth , height ! / viewBoxHeight ) . translate ( rootX , rootY ) ; } else { rootTransform = rootTransform . translate ( - viewBoxX * ( width ! / viewBoxWidth ) , - viewBoxY * ( height ! / viewBoxHeight ) ) . translate ( rootX , rootY ) . scaleNonUniform ( width ! / viewBoxWidth , height ! / viewBoxHeight ) ; } } else { rootTransform = rootTransform . translate ( rootX , rootY ) ; } const combinedMatrix = svgTransform ? svgTransform . matrix . multiply ( rootTransform ) : rootTransform ; svgTransform = context . sourceSvg . createSVGTransformFromMatrix ( combinedMatrix ) ; const children = getNodeChildren ( root ) ; for ( let i = children . length - 1 ; i >= 0 ; i -- ) { const child = children [ i ] as SVGGraphicsElement ; if ( child instanceof SVGSymbolElement || child instanceof SVGMarkerElement ) { continue ; } const childTransform = getCombinedTransform ( context , child , svgTransform ) ; stack . push ( { element : child , transform : childTransform } ) ; } } else { stack . push ( { element : root , transform : svgTransform } ) ; } while ( stack . length > 0 ) { const { element , transform } = stack . pop ( ) ! ; try { drawElement ( context , element , transform ) ; } catch ( e ) { console . error ( e ) ; } if ( element . tagName === 'defs' || element . tagName === 'symbol' || element . tagName === 'marker' || element . tagName === 'svg' || element . tagName === 'clipPath' ) { continue ; } const children = getNodeChildren ( element ) ; for ( let i = children . length - 1 ; i >= 0 ; i -- ) { const childElement = children [ i ] as SVGGraphicsElement ; const newTransform = getCombinedTransform ( context , childElement , transform ) ; stack . push ( { element : childElement , transform : newTransform } ) ; } } } export function drawRoot ( context , element , svgTransform ) { let width = parseFloat ( element . getAttribute ( 'width' ) ! ) ; let height = parseFloat ( element . getAttribute ( 'height' ) ! ) ; if ( isNaN ( width ) || isNaN ( height ) ) { width = height = undefined ; } processRoot ( context , element , svgTransform , width , height ) ; } /**
 * The main switch to delegate drawing of `SVGElement`s
 * to different subroutines.
 */ function drawElement ( context , element , svgTransform ) { if ( isHidden ( element ) ) { return ; } const clipPath = element . getAttribute ( 'clip-path' ) ; if ( clipPath ) { const targetCtx = context . targetCanvasContext ; if ( context . renderMode === RenderMode . CANVAS && targetCtx ) { targetCtx . save ( ) ; } applyClipPath ( context , element , clipPath , svgTransform ) ; } switch ( element . tagName ) { case 'svg' : case 'symbol' : drawRoot ( context , element as SVGSVGElement | SVGSymbolElement , svgTransform ) ; break ; case 'rect' : drawRect ( context , element as SVGRectElement , svgTransform ) ; break ; case 'path' : drawPath ( context , element as SVGPathElement , svgTransform ) ; break ; case 'use' : drawUse ( context , element as SVGUseElement , svgTransform ) ; break ; case 'line' : drawLine ( context , element as SVGLineElement , svgTransform ) ; break ; case 'circle' : drawCircle ( context , element as SVGCircleElement , svgTransform ) ; break ; case 'ellipse' : drawEllipse ( context , element as SVGEllipseElement , svgTransform ) ; break ; case 'polyline' : drawPolyline ( context , element as SVGPolylineElement , svgTransform ) ; break ; case 'polygon' : drawPolygon ( context , element as SVGPolygonElement , svgTransform ) ; break ; case 'text' : drawText ( context , element as SVGTextElement , svgTransform ) ; break ; case 'image' : drawImage ( context , element as SVGImageElement , svgTransform ) ; break ; } if ( clipPath ) { if ( context . renderMode === RenderMode . CANVAS && context . targetCanvasContext ) { context . targetCanvasContext . restore ( ) ; } } }