1/* 2 * Copyright (c) 2024 - 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import type { ImageInfo, ImageData, IAttributes } from '../../../model/Interfaces'; 17import { toUTF8String, getLength } from '../BytesUtils'; 18 19const svgReg = /<svg\s([^>"']|"[^"]*"|'[^']*')*>/; 20const extractorRegExps = { 21 root: svgReg, 22 height: /\sheight=(['"])([^%]+?)\1/, 23 width: /\swidth=(['"])([^%]+?)\1/, 24 viewbox: /\sviewbox=(['"])(.+?)\1/i, 25}; 26 27function parseViewbox(viewbox: string): IAttributes | undefined { 28 const bounds = viewbox.split(' '); 29 let tmpWeight = getLength(bounds[3]); 30 let tmpWidth = getLength(bounds[2]); 31 if (tmpWidth && tmpWeight) { 32 return { height: tmpWeight, width: tmpWidth }; 33 } 34 return undefined; 35} 36 37function getAttirbutes(root: string): IAttributes | undefined { 38 const widths = root.match(extractorRegExps.width); 39 const heights = root.match(extractorRegExps.height); 40 const viewbox = root.match(extractorRegExps.viewbox); 41 if (widths && heights && viewbox) { 42 let tempHeight = getLength(heights[2]); 43 let tmpWidth = getLength(widths[2]); 44 if (tmpWidth && tempHeight) { 45 return { 46 height: heights && tempHeight, 47 viewbox: viewbox && parseViewbox(viewbox[2]), 48 width: widths && tmpWidth, 49 }; 50 } 51 } 52 return undefined; 53} 54 55function calculateByDimensions(attrs: IAttributes): ImageInfo { 56 return { 57 height: attrs.height as number, 58 width: attrs.width as number, 59 }; 60} 61 62function calculateByViewbox(attrs: IAttributes, viewbox: IAttributes): ImageInfo { 63 const ratio = (viewbox.width as number) / (viewbox.height as number); 64 if (attrs.height) { 65 return { 66 height: attrs.height, 67 width: Math.floor(attrs.height * ratio) 68 }; 69 } else if (attrs.width) { 70 return { 71 height: Math.floor(attrs.width * ratio), 72 width: attrs.width, 73 }; 74 } else { 75 return { 76 height: viewbox.height as number, 77 width: viewbox.width as number, 78 }; 79 } 80} 81 82export const SVG: ImageData = { 83 validate: (input) => svgReg.test(toUTF8String(input, 0, 1000)), 84 85 calculate(input) { 86 const root = toUTF8String(input).match(extractorRegExps.root); 87 if (root) { 88 const attrs = getAttirbutes(root[0]); 89 if (attrs) { 90 if (attrs.width && attrs.height) { 91 return calculateByDimensions(attrs); 92 } 93 if (attrs.viewbox) { 94 return calculateByViewbox(attrs, attrs.viewbox); 95 } 96 } 97 } 98 throw new TypeError('Invalid SVG'); 99 }, 100};