import { appendBezierCurve , appendQuadraticCurve , closePath , lineTo , moveTo , } from 'src/api/operators' ; import { PDFOperator } from 'src/core' ; let cx = 0 ; let cy = 0 ; let px = 0 ; let py = 0 ; let sx = 0 ; let sy = 0 ; const parameters = new Map < string , number > ( [ [ 'A' , 7 ] , [ 'a' , 7 ] , [ 'C' , 6 ] , [ 'c' , 6 ] , [ 'H' , 1 ] , [ 'h' , 1 ] , [ 'L' , 2 ] , [ 'l' , 2 ] , [ 'M' , 2 ] , [ 'm' , 2 ] , [ 'Q' , 4 ] , [ 'q' , 4 ] , [ 'S' , 4 ] , [ 's' , 4 ] , [ 'T' , 2 ] , [ 't' , 2 ] , [ 'V' , 1 ] , [ 'v' , 1 ] , [ 'Z' , 0 ] , [ 'z' , 0 ] , ] ) ; interface Cmd { cmd ? : string ; args : number [ ] ; } const parse = ( path ) => { let cmd ; const ret = [ ] ; let args = [ ] ; let curArg = '' ; let foundDecimal = false ; let params = 0 ; for ( const c of path ) { if ( parameters . has ( c ) ) { params = parameters . get ( c ) ! ; if ( cmd ) { if ( curArg . length > 0 ) { args [ args . length ] = + curArg ; } ret [ ret . length ] = { cmd , args } ; args = [ ] ; curArg = '' ; foundDecimal = false ; } cmd = c ; } else if ( [ ' ' , ',' ] . includes ( c ) || ( c === '-' && curArg . length > 0 && curArg [ curArg . length - 1 ] !== 'e' ) || ( c === '.' && foundDecimal ) ) { if ( curArg . length === 0 ) { continue ; } if ( args . length === params ) { ret [ ret . length ] = { cmd , args } ; args = [ + curArg ] ; if ( cmd === 'M' ) { cmd = 'L' ; } if ( cmd === 'm' ) { cmd = 'l' ; } } else { args [ args . length ] = + curArg ; } foundDecimal = c === '.' ; curArg = [ '-' , '.' ] . includes ( c ) ? c : '' ; } else { curArg += c ; if ( c === '.' ) { foundDecimal = true ; } } } if ( curArg . length > 0 ) { if ( args . length === params ) { ret [ ret . length ] = { cmd , args } ; args = [ + curArg ] ; if ( cmd === 'M' ) { cmd = 'L' ; } if ( cmd === 'm' ) { cmd = 'l' ; } } else { args [ args . length ] = + curArg ; } } ret [ ret . length ] = { cmd , args } ; return ret ; } ; const apply = ( commands ) => { cx = cy = px = py = sx = sy = 0 ; let cmds = [ ] ; for ( let i = 0 ; i < commands . length ; i ++ ) { const c = commands [ i ] ; if ( c . cmd && typeof runners [ c . cmd ] === 'function' ) { const cmd = runners [ c . cmd ] ( c . args ) ; if ( Array . isArray ( cmd ) ) { cmds = cmds . concat ( cmd ) ; } else { cmds . push ( cmd ) ; } } } return cmds ; } ; interface CmdToOperatorsMap { [ cmd ] : ( a : number [ ] ) => PDFOperator | PDFOperator [ ] ; } const runners = { M ( a ) { cx = a [ 0 ] ; cy = a [ 1 ] ; px = py = null ; sx = cx ; sy = cy ; return moveTo ( cx , cy ) ; } , m ( a ) { cx += a [ 0 ] ; cy += a [ 1 ] ; px = py = null ; sx = cx ; sy = cy ; return moveTo ( cx , cy ) ; } , C ( a ) { cx = a [ 4 ] ; cy = a [ 5 ] ; px = a [ 2 ] ; py = a [ 3 ] ; return appendBezierCurve ( a [ 0 ] , a [ 1 ] , a [ 2 ] , a [ 3 ] , a [ 4 ] , a [ 5 ] ) ; } , c ( a ) { const cmd = appendBezierCurve ( a [ 0 ] + cx , a [ 1 ] + cy , a [ 2 ] + cx , a [ 3 ] + cy , a [ 4 ] + cx , a [ 5 ] + cy ) ; px = cx + a [ 2 ] ; py = cy + a [ 3 ] ; cx += a [ 4 ] ; cy += a [ 5 ] ; return cmd ; } , S ( a ) { if ( px === null || py === null ) { px = cx ; py = cy ; } const cmd = appendBezierCurve ( cx - ( px - cx ) , cy - ( py - cy ) , a [ 0 ] , a [ 1 ] , a [ 2 ] , a [ 3 ] ) ; px = a [ 0 ] ; py = a [ 1 ] ; cx = a [ 2 ] ; cy = a [ 3 ] ; return cmd ; } , s ( a ) { if ( px === null || py === null ) { px = cx ; py = cy ; } const cmd = appendBezierCurve ( cx - ( px - cx ) , cy - ( py - cy ) , cx + a [ 0 ] , cy + a [ 1 ] , cx + a [ 2 ] , cy + a [ 3 ] ) ; px = cx + a [ 0 ] ; py = cy + a [ 1 ] ; cx += a [ 2 ] ; cy += a [ 3 ] ; return cmd ; } , Q ( a ) { px = a [ 0 ] ; py = a [ 1 ] ; cx = a [ 2 ] ; cy = a [ 3 ] ; return appendQuadraticCurve ( a [ 0 ] , a [ 1 ] , cx , cy ) ; } , q ( a ) { const cmd = appendQuadraticCurve ( a [ 0 ] + cx , a [ 1 ] + cy , a [ 2 ] + cx , a [ 3 ] + cy ) ; px = cx + a [ 0 ] ; py = cy + a [ 1 ] ; cx += a [ 2 ] ; cy += a [ 3 ] ; return cmd ; } , T ( a ) { if ( px === null || py === null ) { px = cx ; py = cy ; } else { px = cx - ( px - cx ) ; py = cy - ( py - cy ) ; } const cmd = appendQuadraticCurve ( px , py , a [ 0 ] , a [ 1 ] ) ; px = cx - ( px - cx ) ; py = cy - ( py - cy ) ; cx = a [ 0 ] ; cy = a [ 1 ] ; return cmd ; } , t ( a ) { if ( px === null || py === null ) { px = cx ; py = cy ; } else { px = cx - ( px - cx ) ; py = cy - ( py - cy ) ; } const cmd = appendQuadraticCurve ( px , py , cx + a [ 0 ] , cy + a [ 1 ] ) ; cx += a [ 0 ] ; cy += a [ 1 ] ; return cmd ; } , A ( a ) { const cmds = solveArc ( cx , cy , a ) ; cx = a [ 5 ] ; cy = a [ 6 ] ; return cmds ; } , a ( a ) { a [ 5 ] += cx ; a [ 6 ] += cy ; const cmds = solveArc ( cx , cy , a ) ; cx = a [ 5 ] ; cy = a [ 6 ] ; return cmds ; } , L ( a ) { cx = a [ 0 ] ; cy = a [ 1 ] ; px = py = null ; return lineTo ( cx , cy ) ; } , l ( a ) { cx += a [ 0 ] ; cy += a [ 1 ] ; px = py = null ; return lineTo ( cx , cy ) ; } , H ( a ) { cx = a [ 0 ] ; px = py = null ; return lineTo ( cx , cy ) ; } , h ( a ) { cx += a [ 0 ] ; px = py = null ; return lineTo ( cx , cy ) ; } , V ( a ) { cy = a [ 0 ] ; px = py = null ; return lineTo ( cx , cy ) ; } , v ( a ) { cy += a [ 0 ] ; px = py = null ; return lineTo ( cx , cy ) ; } , Z ( ) { const cmd = closePath ( ) ; cx = sx ; cy = sy ; return cmd ; } , z ( ) { const cmd = closePath ( ) ; cx = sx ; cy = sy ; return cmd ; } , } ; const solveArc = ( x , y , coords ) => { const [ rx , ry , rot , large , sweep , ex , ey ] = coords ; const segs = arcToSegments ( ex , ey , rx , ry , large , sweep , rot , x , y ) ; const cmds = [ ] ; for ( const seg of segs ) { const bez = segmentToBezier ( ... seg ) ; cmds . push ( appendBezierCurve ( ... bez ) ) ; } return cmds ; } ; type Segment = [ number , number , number , number , number , number , number , number ] ; type Bezier = [ number , number , number , number , number , number ] ; const arcToSegments = ( x , y , rx , ry , large , sweep , rotateX , ox , oy ) => { const th = rotateX * ( Math . PI / 180 ) ; const sinTh = Math . sin ( th ) ; const cosTh = Math . cos ( th ) ; rx = Math . abs ( rx ) ; ry = Math . abs ( ry ) ; px = cosTh * ( ox - x ) * 0.5 + sinTh * ( oy - y ) * 0.5 ; py = cosTh * ( oy - y ) * 0.5 - sinTh * ( ox - x ) * 0.5 ; let pl = ( px * px ) / ( rx * rx ) + ( py * py ) / ( ry * ry ) ; if ( pl > 1 ) { pl = Math . sqrt ( pl ) ; rx *= pl ; ry *= pl ; } const a00 = cosTh / rx ; const a01 = sinTh / rx ; const a10 = - sinTh / ry ; const a11 = cosTh / ry ; const x0 = a00 * ox + a01 * oy ; const y0 = a10 * ox + a11 * oy ; const x1 = a00 * x + a01 * y ; const y1 = a10 * x + a11 * y ; const d = ( x1 - x0 ) * ( x1 - x0 ) + ( y1 - y0 ) * ( y1 - y0 ) ; let sfactorSq = 1 / d - 0.25 ; if ( sfactorSq < 0 ) { sfactorSq = 0 ; } let sfactor = Math . sqrt ( sfactorSq ) ; if ( sweep === large ) { sfactor = - sfactor ; } const xc = 0.5 * ( x0 + x1 ) - sfactor * ( y1 - y0 ) ; const yc = 0.5 * ( y0 + y1 ) + sfactor * ( x1 - x0 ) ; const th0 = Math . atan2 ( y0 - yc , x0 - xc ) ; const th1 = Math . atan2 ( y1 - yc , x1 - xc ) ; let thArc = th1 - th0 ; if ( thArc < 0 && sweep === 1 ) { thArc += 2 * Math . PI ; } else if ( thArc > 0 && sweep === 0 ) { thArc -= 2 * Math . PI ; } const segments = Math . ceil ( Math . abs ( thArc / ( Math . PI * 0.5 + 0.001 ) ) ) ; const result = [ ] ; for ( let i = 0 ; i < segments ; i ++ ) { const th2 = th0 + ( i * thArc ) / segments ; const th3 = th0 + ( ( i + 1 ) * thArc ) / segments ; result [ i ] = [ xc , yc , th2 , th3 , rx , ry , sinTh , cosTh ] ; } return result ; } ; const segmentToBezier = ( cx1 , cy1 , th0 , th1 , rx , ry , sinTh , cosTh ) => { const a00 = cosTh * rx ; const a01 = - sinTh * ry ; const a10 = sinTh * rx ; const a11 = cosTh * ry ; const thHalf = 0.5 * ( th1 - th0 ) ; const t = ( ( 8 / 3 ) * Math . sin ( thHalf * 0.5 ) * Math . sin ( thHalf * 0.5 ) ) / Math . sin ( thHalf ) ; const x1 = cx1 + Math . cos ( th0 ) - t * Math . sin ( th0 ) ; const y1 = cy1 + Math . sin ( th0 ) + t * Math . cos ( th0 ) ; const x3 = cx1 + Math . cos ( th1 ) ; const y3 = cy1 + Math . sin ( th1 ) ; const x2 = x3 + t * Math . sin ( th1 ) ; const y2 = y3 - t * Math . cos ( th1 ) ; const result = [ a00 * x1 + a01 * y1 , a10 * x1 + a11 * y1 , a00 * x2 + a01 * y2 , a10 * x2 + a11 * y2 , a00 * x3 + a01 * y3 , a10 * x3 + a11 * y3 , ] ; return result ; } ; export const svgPathToOperators = ( path ) => apply ( parse ( path ) ) ;