Path: blob/main/extensions/copilot/src/util/common/imageUtils.ts
13397 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*--------------------------------------------------------------------------------------------*/456export function getImageDimensions(base64: string) {7if (!base64.startsWith('data:image/')) {8throw new Error('Could not read image: invalid base64 image string');9}10const rawString = base64.split(',')[1];11switch (getMimeType(rawString)) {12case 'image/png':13return getPngDimensions(rawString);14case 'image/gif':15return getGifDimensions(rawString);16case 'image/jpeg':17case 'image/jpg':18return getJpegDimensions(rawString);19case 'image/webp':20return getWebPDimensions(rawString);21default:22throw new Error('Unsupported image format');23}24}2526export function getPngDimensions(base64: string) {27const header = atob(base64.slice(0, 50)).slice(16, 24);28const uint8 = Uint8Array.from(header, c => c.charCodeAt(0));29const dataView = new DataView(uint8.buffer);3031return {32width: dataView.getUint32(0, false),33height: dataView.getUint32(4, false)34};35}3637export function getGifDimensions(base64: string) {38const header = atob(base64.slice(0, 50));39const uint8 = Uint8Array.from(header, c => c.charCodeAt(0));40const dataView = new DataView(uint8.buffer);4142return {43width: dataView.getUint16(6, true),44height: dataView.getUint16(8, true)45};46}4748export function getJpegDimensions(base64: string) {49const binary = atob(base64);50const uint8 = Uint8Array.from(binary, c => c.charCodeAt(0));51const length = uint8.length;52let offset = 2;5354while (offset < length) {55const marker = (uint8[offset] << 8) | uint8[offset + 1];56const segmentLength = (uint8[offset + 2] << 8) | uint8[offset + 3];5758if (marker >= 0xFFC0 && marker <= 0xFFC2) {59const dataView = new DataView(uint8.buffer, offset + 5, 4);60return {61height: dataView.getUint16(0, false),62width: dataView.getUint16(2, false)63};64}6566offset += 2 + segmentLength;67}6869throw new Error('JPEG dimensions not found');70}7172export function getWebPDimensions(base64String: string) {73const binaryString = atob(base64String);74const binaryData = new Uint8Array(binaryString.length);75for (let i = 0; i < binaryString.length; i++) {76binaryData[i] = binaryString.charCodeAt(i);77}7879if (binaryString.slice(0, 4) !== 'RIFF' || binaryString.slice(8, 12) !== 'WEBP') {80throw new Error('Not a valid WebP image.');81}8283const chunkHeader = binaryString.slice(12, 16);8485if (chunkHeader === 'VP8 ') {86const width = (binaryData[26] | (binaryData[27] << 8)) & 0x3FFF;87const height = (binaryData[28] | (binaryData[29] << 8)) & 0x3FFF;88return { width, height };89} else if (chunkHeader === 'VP8L') {90const width = (binaryData[21] | (binaryData[22] << 8)) & 0x3FFF;91const height = (binaryData[23] | (binaryData[24] << 8)) & 0x3FFF;92return { width, height };93} else if (chunkHeader === 'VP8X') {94const width = ((binaryData[24] | (binaryData[25] << 8) | (binaryData[26] << 16)) & 0xFFFFFF) + 1;95const height = ((binaryData[27] | (binaryData[28] << 8) | (binaryData[29] << 16)) & 0xFFFFFF) + 1;96return { width, height };97} else {98throw new Error('Unsupported WebP format.');99}100}101102export function getMimeType(base64String: string): string | undefined {103const mimeTypes: { [key: string]: string } = {104'/9j/': 'image/jpeg',105'iVBOR': 'image/png',106'R0lGOD': 'image/gif',107'UklGR': 'image/webp',108};109110for (const prefix of Object.keys(mimeTypes)) {111if (base64String.startsWith(prefix)) {112return mimeTypes[prefix];113}114}115}116117export function extractImageAttributes(line: string, refineExisting?: boolean): string | undefined {118// Regex to match markdown image syntax 119const markdownImageRegex = /!\[([^\]]*)\]\(<?([^)<>]+?)>?\)/;120// Updated regex to match HTML image syntax with alt and src in any order121const htmlImageRegex = /<img\s+(?:alt=["']([^"']*)["']\s*)?src=["']([^"']+)["'](?:\s*alt=["']([^"']*)["'])?/;122123let match;124let imagePath = '';125let altText = '';126127if ((match = markdownImageRegex.exec(line)) !== null) {128imagePath = match[2];129altText = match[1];130} else if ((match = htmlImageRegex.exec(line)) !== null) {131imagePath = match[2]; // src is always the second group132altText = match[1] || match[3] || ''; // alt is sometimes first or third133} else {134// Try Learn Markdown format - check if it's a Learn Markdown image135const learnMarkdownRegex = /:::image\s+.*?source=["']([^"']+)["'].*?:::/;136const sourceMatch = learnMarkdownRegex.exec(line);137if (sourceMatch) {138imagePath = sourceMatch[1];139// Check if there's an alt-text attribute140const altTextRegex = /alt-text=["']([^"']*?)["']/;141const altMatch = altTextRegex.exec(line);142altText = altMatch ? altMatch[1] : '';143} else {144return undefined;145}146}147148if (refineExisting ? !altText : !!altText) {149return undefined;150}151152return imagePath;153}154155156