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 fs from 'fs'; 17import { BMP } from './imageFormat/bmp'; 18import { JPG } from './imageFormat/jpg'; 19import { PNG } from './imageFormat/png'; 20import { SVG } from './imageFormat/svg'; 21import { WEBP } from './imageFormat/webp'; 22import { ImageInfo } from '../../model/Interfaces'; 23 24const MaxInputSize = 512 * 1024; 25const typeHandlers = { 26 bmp: BMP, 27 jpg: JPG, 28 png: PNG, 29 svg: SVG, 30 webp: WEBP, 31}; 32 33export type imageType = keyof typeof typeHandlers; 34 35const firstBytes: Record<number, imageType> = { 36 0x42: 'bmp', 37 0x52: 'webp', 38 0x89: 'png', 39 0xff: 'jpg', 40}; 41const keys = Object.keys(typeHandlers) as imageType[]; 42 43export function readImageInfo(filePath: string): ImageInfo | undefined { 44 const input = readFileSync(filePath); 45 if (!input) { return undefined; } 46 const type = detector(input); 47 if (typeof type === 'undefined') { 48 return undefined; 49 } 50 if (type in typeHandlers) { 51 const size = typeHandlers[type].calculate(input, filePath); 52 if (!size) { 53 return undefined; 54 } 55 let imageInfo = { width: size.width, height: size.height, type: '' }; 56 imageInfo.type = size.type ?? type; 57 if (size.images && size.images.length > 1) { 58 const largestImage = size.images.reduce((largest, current) => { 59 return current.width * current.height > largest.width * largest.height ? current : largest; 60 }, size.images[0]); 61 imageInfo.width = largestImage.width; 62 imageInfo.height = largestImage.height; 63 } 64 return imageInfo; 65 } 66 return undefined; 67} 68 69function readFileSync(filepath: string): Uint8Array | undefined { 70 const descriptor = fs.openSync(filepath, 'r'); 71 try { 72 const { size } = fs.fstatSync(descriptor); 73 if (size <= 0) { 74 return undefined; 75 } 76 const inputSize = Math.min(size, MaxInputSize); 77 const input = new Uint8Array(inputSize); 78 fs.readSync(descriptor, input, 0, inputSize, 0); 79 return input; 80 } finally { 81 fs.closeSync(descriptor); 82 } 83} 84 85function detector(input: Uint8Array): imageType | undefined { 86 try { 87 const byte = input[0]; 88 if (byte in firstBytes) { 89 const type = firstBytes[byte]; 90 if (type && typeHandlers[type].validate(input)) { 91 return type; 92 } 93 } 94 const finder = (key: imageType): boolean => typeHandlers[key].validate(input); 95 return keys.find(finder); 96 } catch (error) { 97 return undefined; 98 } 99}