• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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};