| // TODO | 
| // 1. shadow | 
| // 2. Image: sx, sy, sw, sh | 
|   | 
| import {createElement, XLINKNS } from '../svg/core'; | 
| import { getMatrixStr, TEXT_ALIGN_TO_ANCHOR, adjustTextY } from '../svg/helper'; | 
| import * as matrix from '../core/matrix'; | 
| import Path, { PathStyleProps } from '../graphic/Path'; | 
| import ZRImage, { ImageStyleProps } from '../graphic/Image'; | 
| import { getLineHeight } from '../contain/text'; | 
| import TSpan, { TSpanStyleProps } from '../graphic/TSpan'; | 
| import SVGPathRebuilder from '../svg/SVGPathRebuilder'; | 
| import mapStyleToAttrs from '../svg/mapStyleToAttrs'; | 
| import { DEFAULT_FONT } from '../core/platform'; | 
|   | 
| export interface SVGProxy<T> { | 
|     brush(el: T): void | 
| } | 
|   | 
| type AllStyleOption = PathStyleProps | TSpanStyleProps | ImageStyleProps; | 
|   | 
| function setTransform(svgEl: SVGElement, m: matrix.MatrixArray) { | 
|     if (m) { | 
|         attr(svgEl, 'transform', getMatrixStr(m)); | 
|     } | 
| } | 
|   | 
| function attr(el: SVGElement, key: string, val: string | number) { | 
|     if (!val || (val as any).type !== 'linear' && (val as any).type !== 'radial') { | 
|         // Don't set attribute for gradient, since it need new dom nodes | 
|         el.setAttribute(key, val as any); | 
|     } | 
| } | 
|   | 
| function attrXLink(el: SVGElement, key: string, val: string) { | 
|     el.setAttributeNS(XLINKNS, key, val); | 
| } | 
|   | 
| function attrXML(el: SVGElement, key: string, val: string) { | 
|     el.setAttributeNS('http://www.w3.org/XML/1998/namespace', key, val); | 
| } | 
|   | 
| function bindStyle(svgEl: SVGElement, style: PathStyleProps, el?: Path): void | 
| function bindStyle(svgEl: SVGElement, style: TSpanStyleProps, el?: TSpan): void | 
| function bindStyle(svgEl: SVGElement, style: ImageStyleProps, el?: ZRImage): void | 
| function bindStyle(svgEl: SVGElement, style: AllStyleOption, el?: Path | TSpan | ZRImage) { | 
|     mapStyleToAttrs((key, val) => attr(svgEl, key, val), style, el, true); | 
| } | 
|   | 
| interface PathWithSVGBuildPath extends Path { | 
|     __svgPathVersion: number | 
|     __svgPathBuilder: SVGPathRebuilder | 
| } | 
|   | 
| const svgPath: SVGProxy<Path> = { | 
|     brush(el: Path) { | 
|         const style = el.style; | 
|   | 
|         let svgEl = el.__svgEl; | 
|         if (!svgEl) { | 
|             svgEl = createElement('path'); | 
|             el.__svgEl = svgEl; | 
|         } | 
|   | 
|         if (!el.path) { | 
|             el.createPathProxy(); | 
|         } | 
|         const path = el.path; | 
|   | 
|         if (el.shapeChanged()) { | 
|             path.beginPath(); | 
|             el.buildPath(path, el.shape); | 
|             el.pathUpdated(); | 
|         } | 
|   | 
|         const pathVersion = path.getVersion(); | 
|         const elExt = el as PathWithSVGBuildPath; | 
|         let svgPathBuilder = elExt.__svgPathBuilder; | 
|         if (elExt.__svgPathVersion !== pathVersion || !svgPathBuilder || el.style.strokePercent < 1) { | 
|             if (!svgPathBuilder) { | 
|                 svgPathBuilder = elExt.__svgPathBuilder = new SVGPathRebuilder(); | 
|             } | 
|             svgPathBuilder.reset(); | 
|             path.rebuildPath(svgPathBuilder, el.style.strokePercent); | 
|             svgPathBuilder.generateStr(); | 
|             elExt.__svgPathVersion = pathVersion; | 
|         } | 
|   | 
|         attr(svgEl, 'd', svgPathBuilder.getStr()); | 
|   | 
|         bindStyle(svgEl, style, el); | 
|         setTransform(svgEl, el.transform); | 
|     } | 
| }; | 
|   | 
| export {svgPath as path}; | 
|   | 
| /*************************************************** | 
|  * IMAGE | 
|  **************************************************/ | 
| const svgImage: SVGProxy<ZRImage> = { | 
|     brush(el: ZRImage) { | 
|         const style = el.style; | 
|         let image = style.image; | 
|   | 
|         if (image instanceof HTMLImageElement) { | 
|             image = image.src; | 
|         } | 
|         // heatmap layer in geo may be a canvas | 
|         else if (image instanceof HTMLCanvasElement) { | 
|             image = image.toDataURL(); | 
|         } | 
|         if (!image) { | 
|             return; | 
|         } | 
|   | 
|         const x = style.x || 0; | 
|         const y = style.y || 0; | 
|   | 
|         const dw = style.width; | 
|         const dh = style.height; | 
|   | 
|         let svgEl = el.__svgEl; | 
|         if (!svgEl) { | 
|             svgEl = createElement('image'); | 
|             el.__svgEl = svgEl; | 
|         } | 
|   | 
|         if (image !== el.__imageSrc) { | 
|             attrXLink(svgEl, 'href', image as string); | 
|             // Caching image src | 
|             el.__imageSrc = image as string; | 
|         } | 
|   | 
|         attr(svgEl, 'width', dw + ''); | 
|         attr(svgEl, 'height', dh + ''); | 
|   | 
|         attr(svgEl, 'x', x + ''); | 
|         attr(svgEl, 'y', y + ''); | 
|   | 
|         bindStyle(svgEl, style, el); | 
|         setTransform(svgEl, el.transform); | 
|     } | 
| }; | 
| export {svgImage as image}; | 
|   | 
| /*************************************************** | 
|  * TEXT | 
|  **************************************************/ | 
|   | 
|   | 
| const svgText: SVGProxy<TSpan> = { | 
|     brush(el: TSpan) { | 
|         const style = el.style; | 
|   | 
|         let text = style.text; | 
|         // Convert to string | 
|         text != null && (text += ''); | 
|         if (!text || isNaN(style.x) || isNaN(style.y)) { | 
|             return; | 
|         } | 
|   | 
|         let textSvgEl = el.__svgEl as SVGTextElement; | 
|         if (!textSvgEl) { | 
|             textSvgEl = createElement('text') as SVGTextElement; | 
|             attrXML(textSvgEl, 'xml:space', 'preserve'); | 
|             el.__svgEl = textSvgEl; | 
|         } | 
|   | 
|         const font = style.font || DEFAULT_FONT; | 
|   | 
|         // style.font has been normalized by `normalizeTextStyle`. | 
|         const textSvgElStyle = textSvgEl.style; | 
|         textSvgElStyle.font = font; | 
|   | 
|         textSvgEl.textContent = text; | 
|   | 
|         bindStyle(textSvgEl, style, el); | 
|         setTransform(textSvgEl, el.transform); | 
|   | 
|         // Consider different font display differently in vertial align, we always | 
|         // set vertialAlign as 'middle', and use 'y' to locate text vertically. | 
|         const x = style.x || 0; | 
|         const y = adjustTextY(style.y || 0, getLineHeight(font), style.textBaseline); | 
|         const textAlign = TEXT_ALIGN_TO_ANCHOR[style.textAlign as keyof typeof TEXT_ALIGN_TO_ANCHOR] | 
|             || style.textAlign; | 
|   | 
|         attr(textSvgEl, 'dominant-baseline', 'central'); | 
|         attr(textSvgEl, 'text-anchor', textAlign); | 
|         attr(textSvgEl, 'x', x + ''); | 
|         attr(textSvgEl, 'y', y + ''); | 
|     } | 
| }; | 
| export {svgText as text}; |