Path: blob/main/extensions/emmet/src/imageSizeHelper.ts
5241 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45// Based on @sergeche's work on the emmet plugin for atom67import * as path from 'path';8import * as http from 'http';9import * as https from 'https';10import { URL } from 'url';11import { imageSize } from 'image-size';12import { ISizeCalculationResult } from 'image-size/dist/types/interface';1314const reUrl = /^https?:/;15export type ImageInfoWithScale = {16realWidth: number;17realHeight: number;18width: number;19height: number;20};2122/**23* Get size of given image file. Supports files from local filesystem,24* as well as URLs25*/26export function getImageSize(file: string): Promise<ImageInfoWithScale | undefined> {27file = file.replace(/^file:\/\//, '');28return reUrl.test(file) ? getImageSizeFromURL(file) : getImageSizeFromFile(file);29}3031/**32* Get image size from file on local file system33*/34function getImageSizeFromFile(file: string): Promise<ImageInfoWithScale | undefined> {35return new Promise((resolve, reject) => {36const isDataUrl = file.match(/^data:.+?;base64,/);3738if (isDataUrl) {39// NB should use sync version of `sizeOf()` for buffers40try {41const data = Buffer.from(file.slice(isDataUrl[0].length), 'base64');42return resolve(sizeForFileName('', imageSize(data)));43} catch (err) {44return reject(err);45}46}4748imageSize(file, (err: Error | null, size?: ISizeCalculationResult) => {49if (err) {50reject(err);51} else {52resolve(sizeForFileName(path.basename(file), size));53}54});55});56}5758/**59* Get image size from given remove URL60*/61function getImageSizeFromURL(urlStr: string): Promise<ImageInfoWithScale | undefined> {62return new Promise((resolve, reject) => {63const url = new URL(urlStr);64const getTransport = url.protocol === 'https:' ? https.get : http.get;6566if (!url.pathname) {67return reject('Given url doesnt have pathname property');68}69const urlPath: string = url.pathname;7071getTransport(url, resp => {72const chunks: Buffer[] = [];73let bufSize = 0;7475const trySize = (chunks: Buffer[]) => {76try {77const size: ISizeCalculationResult = imageSize(Buffer.concat(chunks, bufSize));78resp.removeListener('data', onData);79resp.destroy(); // no need to read further80resolve(sizeForFileName(path.basename(urlPath), size));81} catch (err) {82// might not have enough data, skip error83}84};8586const onData = (chunk: Buffer) => {87bufSize += chunk.length;88chunks.push(chunk);89trySize(chunks);90};9192resp93.on('data', onData)94.on('end', () => trySize(chunks))95.once('error', err => {96resp.removeListener('data', onData);97reject(err);98});99}).once('error', reject);100});101}102103/**104* Returns size object for given file name. If file name contains `@Nx` token,105* the final dimentions will be downscaled by N106*/107function sizeForFileName(fileName: string, size?: ISizeCalculationResult): ImageInfoWithScale | undefined {108const m = fileName.match(/@(\d+)x\./);109const scale = m ? +m[1] : 1;110111if (!size || !size.width || !size.height) {112return;113}114115return {116realWidth: size.width,117realHeight: size.height,118width: Math.floor(size.width / scale),119height: Math.floor(size.height / scale)120};121}122123124