| // Shared methods of svg and svg-ssr | 
|   | 
| import { MatrixArray } from '../core/matrix'; | 
| import Transformable, { TransformProp } from '../core/Transformable'; | 
| import { RADIAN_TO_DEGREE, retrieve2, logError, isFunction } from '../core/util'; | 
| import Displayable from '../graphic/Displayable'; | 
| import { GradientObject } from '../graphic/Gradient'; | 
| import { LinearGradientObject } from '../graphic/LinearGradient'; | 
| import Path from '../graphic/Path'; | 
| import { ImagePatternObject, PatternObject, SVGPatternObject } from '../graphic/Pattern'; | 
| import { RadialGradientObject } from '../graphic/RadialGradient'; | 
| import { parse } from '../tool/color'; | 
| import env from '../core/env'; | 
|   | 
| const mathRound = Math.round; | 
|   | 
| export function normalizeColor(color: string): { color: string; opacity: number; } { | 
|     let opacity; | 
|     if (!color || color === 'transparent') { | 
|         color = 'none'; | 
|     } | 
|     else if (typeof color === 'string' && color.indexOf('rgba') > -1) { | 
|         const arr = parse(color); | 
|         if (arr) { | 
|             // TODO use hex? | 
|             color = 'rgb(' + arr[0] + ',' + arr[1] + ',' + arr[2] + ')'; | 
|             opacity = arr[3]; | 
|         } | 
|     } | 
|     return { | 
|         color, | 
|         opacity: opacity == null ? 1 : opacity | 
|     }; | 
| } | 
| const EPSILON = 1e-4; | 
| export function isAroundZero(transform: number) { | 
|     return transform < EPSILON && transform > -EPSILON; | 
| } | 
|   | 
| export function round3(transform: number) { | 
|     return mathRound(transform * 1e3) / 1e3; | 
| } | 
| export function round4(transform: number) { | 
|     return mathRound(transform * 1e4) / 1e4; | 
| } | 
| export function round1(transform: number) { | 
|     return mathRound(transform * 10) / 10; | 
| } | 
|   | 
| export function getMatrixStr(m: MatrixArray) { | 
|     return 'matrix(' | 
|         // Avoid large string of matrix | 
|         // PENDING If have precision issue when scaled | 
|         + round3(m[0]) + ',' | 
|         + round3(m[1]) + ',' | 
|         + round3(m[2]) + ',' | 
|         + round3(m[3]) + ',' | 
|         + round4(m[4]) + ',' | 
|         + round4(m[5]) | 
|         + ')'; | 
| } | 
|   | 
| export const TEXT_ALIGN_TO_ANCHOR = { | 
|     left: 'start', | 
|     right: 'end', | 
|     center: 'middle', | 
|     middle: 'middle' | 
| }; | 
|   | 
| export function adjustTextY(y: number, lineHeight: number, textBaseline: CanvasTextBaseline): number { | 
|     // TODO Other baselines. | 
|     if (textBaseline === 'top') { | 
|         y += lineHeight / 2; | 
|     } | 
|     else if (textBaseline === 'bottom') { | 
|         y -= lineHeight / 2; | 
|     } | 
|     return y; | 
| } | 
|   | 
|   | 
| export function hasShadow(style: Displayable['style']) { | 
|     // TODO: textBoxShadowBlur is not supported yet | 
|     return style | 
|         && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY); | 
| } | 
|   | 
| export function getShadowKey(displayable: Displayable) { | 
|     const style = displayable.style; | 
|     const globalScale = displayable.getGlobalScale(); | 
|     return [ | 
|         style.shadowColor, | 
|         (style.shadowBlur || 0).toFixed(2), // Reduce the precision | 
|         (style.shadowOffsetX || 0).toFixed(2), | 
|         (style.shadowOffsetY || 0).toFixed(2), | 
|         globalScale[0], | 
|         globalScale[1] | 
|     ].join(','); | 
| } | 
|   | 
| export function getClipPathsKey(clipPaths: Path[]) { | 
|     let key: number[] = []; | 
|     if (clipPaths) { | 
|         for (let i = 0; i < clipPaths.length; i++) { | 
|             const clipPath = clipPaths[i]; | 
|             key.push(clipPath.id); | 
|         } | 
|     } | 
|     return key.join(','); | 
| } | 
|   | 
| export function isImagePattern(val: any): val is ImagePatternObject { | 
|     return val && (!!(val as ImagePatternObject).image); | 
| } | 
| export function isSVGPattern(val: any): val is SVGPatternObject { | 
|     return val && (!!(val as SVGPatternObject).svgElement); | 
| } | 
| export function isPattern(val: any): val is PatternObject { | 
|     return isImagePattern(val) || isSVGPattern(val); | 
| } | 
|   | 
| export function isLinearGradient(val: GradientObject): val is LinearGradientObject { | 
|     return val.type === 'linear'; | 
| } | 
|   | 
| export function isRadialGradient(val: GradientObject): val is RadialGradientObject { | 
|     return val.type === 'radial'; | 
| } | 
|   | 
| export function isGradient(val: any): val is GradientObject { | 
|     return val && ( | 
|         (val as GradientObject).type === 'linear' | 
|         || (val as GradientObject).type === 'radial' | 
|     ); | 
| } | 
|   | 
| export function getIdURL(id: string) { | 
|     return `url(#${id})`; | 
| } | 
|   | 
| export function getPathPrecision(el: Path) { | 
|     const scale = el.getGlobalScale(); | 
|     const size = Math.max(scale[0], scale[1]); | 
|     return Math.max(Math.ceil(Math.log(size) / Math.log(10)), 1); | 
| } | 
|   | 
| export function getSRTTransformString( | 
|     transform: Partial<Pick<Transformable, TransformProp>> | 
| ) { | 
|     const x = transform.x || 0; | 
|     const y = transform.y || 0; | 
|     const rotation = (transform.rotation || 0) * RADIAN_TO_DEGREE; | 
|     const scaleX = retrieve2(transform.scaleX, 1); | 
|     const scaleY = retrieve2(transform.scaleY, 1); | 
|     const skewX = transform.skewX || 0; | 
|     const skewY = transform.skewY || 0; | 
|     const res = []; | 
|     if (x || y) { | 
|         // TODO not using px unit? | 
|         res.push(`translate(${x}px,${y}px)`); | 
|     } | 
|     if (rotation) { | 
|         res.push(`rotate(${rotation})`); | 
|     } | 
|     if (scaleX !== 1 || scaleY !== 1) { | 
|         res.push(`scale(${scaleX},${scaleY})`); | 
|     } | 
|     if (skewX || skewY) { | 
|         res.push(`skew(${mathRound(skewX * RADIAN_TO_DEGREE)}deg, ${mathRound(skewY * RADIAN_TO_DEGREE)}deg)`); | 
|     } | 
|   | 
|     return res.join(' '); | 
| } | 
|   | 
| export const encodeBase64 = (function () { | 
|     if (env.hasGlobalWindow && isFunction(window.btoa)) { | 
|         return function (str: string) { | 
|             return window.btoa(unescape(encodeURIComponent(str))); | 
|         }; | 
|     } | 
|     if (typeof Buffer !== 'undefined') { | 
|         return function (str: string) { | 
|             return Buffer.from(str).toString('base64'); | 
|         }; | 
|     } | 
|     return function (str: string): string { | 
|         if (process.env.NODE_ENV !== 'production') { | 
|             logError('Base64 isn\'t natively supported in the current environment.'); | 
|         } | 
|         return null; | 
|     }; | 
| })(); |