/* * (c) Copyright Ascensio System SIA 2010-2024 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation. In accordance with * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect * that Ascensio System SIA expressly excludes the warranty of non-infringement * of any third-party rights. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish * street, Riga, Latvia, EU, LV-1050. * * The interactive user interfaces in modified source and object code versions * of the Program must display Appropriate Legal Notices, as required under * Section 5 of the GNU AGPL version 3. * * Pursuant to Section 7(b) of the License you must retain the original Product * logo when distributing the program. Pursuant to Section 7(e) we decline to * grant you any rights under trademark law for use of our trademarks. * * All the Product's GUI elements, including illustrations and icon sets, as * well as technical writing content are licensed under the terms of the * Creative Commons Attribution-ShareAlike 4.0 International. See the License * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode * */ 'use strict'; const constants = require('./constants'); const {open} = require('node:fs/promises'); function getImageFormatBySignature(buffer) { const length = buffer.length; //1000 for svg(xml header and creator comment) const startText = buffer.toString('ascii', 0, 1000); //jpeg // Hex: FF D8 FF if (3 <= length && 0xff == buffer[0] && 0xd8 == buffer[1] && 0xff == buffer[2]) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_JPG; } //bmp ( http://ru.wikipedia.org/wiki/BMP ) //Hex: 42 4D //ASCII: BM //Hex (position 6) : 00 00 //Hex (position 26): 01 00 //Hex (position 28): 00 || 01 || 04 || 08 || 10 || 18 || 20 //Hex (position 29): 00 //Hex (position 30): 00 || 01 || 02 || 03 || 04 || 05 //Hex (position 31): 00 00 00 if ( 34 <= length && 0x42 == buffer[0] && 0x4d == buffer[1] && 0x00 == buffer[6] && 0x00 == buffer[7] && 0x01 == buffer[26] && 0x00 == buffer[27] && (0x00 == buffer[28] || 0x01 == buffer[28] || 0x04 == buffer[28] || 0x08 == buffer[28] || 0x10 == buffer[28] || 0x18 == buffer[28] || 0x20 == buffer[28]) && 0x00 == buffer[29] && (0x00 == buffer[30] || 0x01 == buffer[30] || 0x02 == buffer[30] || 0x03 == buffer[30] || 0x04 == buffer[30] || 0x05 == buffer[30]) && 0x00 == buffer[31] && 0x00 == buffer[32] && 0x00 == buffer[33] ) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_BMP; } //gif //Hex: 47 49 46 38 //ASCII: GIF8 //or for GIF87a... //Hex: 47 49 46 38 37 61 //ASCII: GIF87a //or for GIF89a... //Hex: 47 49 46 38 39 61 //ASCII: GIF89a if (0 == startText.indexOf('GIF8')) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_GIF; } if (0 == startText.indexOf('GIF87a') || 0 == startText.indexOf('GIF89a')) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_GIF; } //png //Hex: 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 //ASCII: .PNG........IHDR if ( 16 <= length && 0x89 == buffer[0] && 0x50 == buffer[1] && 0x4e == buffer[2] && 0x47 == buffer[3] && 0x0d == buffer[4] && 0x0a == buffer[5] && 0x1a == buffer[6] && 0x0a == buffer[7] && 0x00 == buffer[8] && 0x00 == buffer[9] && 0x00 == buffer[10] && 0x0d == buffer[11] && 0x49 == buffer[12] && 0x48 == buffer[13] && 0x44 == buffer[14] && 0x52 == buffer[15] ) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_PNG; } //CR2 //Hex: 49 49 2A 00 10 00 00 00 43 52 //ASCII: II*.....CR if ( 10 <= length && 0x49 == buffer[0] && 0x49 == buffer[1] && 0x2a == buffer[2] && 0x00 == buffer[3] && 0x10 == buffer[4] && 0x00 == buffer[5] && 0x00 == buffer[6] && 0x00 == buffer[7] && 0x43 == buffer[8] && 0x52 == buffer[9] ) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_CR2; } //tiff //Hex: 49 49 2A 00 //ASCII: //or for big endian //Hex: 4D 4D 00 2A //ASCII: MM.* //or for little endian //Hex: 49 49 2A 00 //ASCII: II* if (4 <= length) { if ( (0x49 == buffer[0] && 0x49 == buffer[1] && 0x2a == buffer[2] && 0x00 == buffer[3]) || (0x4d == buffer[0] && 0x4d == buffer[1] && 0x00 == buffer[2] && 0x2a == buffer[3]) || (0x49 == buffer[0] && 0x49 == buffer[1] && 0x2a == buffer[2] && 0x00 == buffer[3]) ) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_TIFF; } } //wmf //Hex: D7 CD C6 9A 00 00 //or for Windows 3.x //Hex: 01 00 09 00 00 03 if (6 <= length) { if ( (0xd7 == buffer[0] && 0xcd == buffer[1] && 0xc6 == buffer[2] && 0x9a == buffer[3] && 0x00 == buffer[4] && 0x00 == buffer[5]) || (0x01 == buffer[0] && 0x00 == buffer[1] && 0x09 == buffer[2] && 0x00 == buffer[3] && 0x00 == buffer[4] && 0x03 == buffer[5]) ) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_WMF; } } //emf ( http://wvware.sourceforge.net/caolan/ora-wmf.html ) //Hex: 01 00 00 00 //Hex (position 40): 20 45 4D 46 if ( 44 <= length && 0x01 == buffer[0] && 0x00 == buffer[1] && 0x00 == buffer[2] && 0x00 == buffer[3] && 0x20 == buffer[40] && 0x45 == buffer[41] && 0x4d == buffer[42] && 0x46 == buffer[43] ) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_EMF; } //pcx ( http://www.fileformat.info/format/pcx/corion.htm ) //Hex (position 0): 0A //Hex (position 1): 00 || 01 || 02 || 03 || 04 || 05 //Hex (position 3): 01 || 02 || 04 || 08 ( Bytes per pixel ) if ( 4 <= length && 0x0a == buffer[0] && (0x00 == buffer[1] || 0x01 == buffer[1] || 0x02 == buffer[1] || 0x03 == buffer[1] || 0x04 == buffer[1] || 0x05 == buffer[1]) && (0x01 == buffer[3] || 0x02 == buffer[3] || 0x04 == buffer[3] || 0x08 == buffer[3]) ) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_PCX; } //tga ( http://www.fileformat.info/format/tga/corion.htm ) //DATA TYPE 1-COLOR-MAPPED IMAGES : Hex (position 1) : 01 01 //DATA TYPE 2-TRUE-COLOR IMAGES : Hex (position 1) : 00 02 //DATA TYPE 3-BLACK AND WHITE(UNMAPPED) IMAGES : Hex (position 1) : 00 03 //DATA TYPE 9-RUN-LENGTH ENCODED(RLE),COLOR-MAPPED IMAGES : Hex (position 1) : 01 09 //DATA TYPE 10-RUN-LENGTH ENCODED(RLE),TRUE-COLOR IMAGES : Hex (position 1) : 00 0A //DATA TYPE 11-RUN-LENGTH ENCODED(RLE),BLACK AND WHITE IMAGES : Hex (position 1) : 00 0B // + Bytes per pixel : Hex (position 16): 0x08 || 0x10 || 0x18 || 0x20 if ( 17 <= length && ((0x01 == buffer[1] && 0x01 == buffer[2]) || (0x00 == buffer[1] && 0x02 == buffer[2]) || (0x00 == buffer[1] && 0x03 == buffer[2]) || (0x01 == buffer[1] && 0x09 == buffer[2]) || (0x00 == buffer[1] && 0x0a == buffer[2]) || (0x00 == buffer[1] && 0x0b == buffer[2])) && (0x08 == buffer[16] || 0x10 == buffer[16] || 0x18 == buffer[16] || 0x20 == buffer[16]) ) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_TGA; } //ras //Hex: 59 A6 6A 95 //ASCII: Y if (4 <= length && 0x59 == buffer[0] && 0xa6 == buffer[1] && 0x6a == buffer[2] && 0x95 == buffer[3]) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_RAS; } //ipod //(None or Unknown) //psd //Hex: 38 42 50 53 00 01 00 00 00 00 00 00 00 //ASCII: 8BPS if ( 13 <= length && 0x38 == buffer[0] && 0x42 == buffer[1] && 0x50 == buffer[2] && 0x53 == buffer[3] && 0x00 == buffer[4] && 0x01 == buffer[5] && 0x00 == buffer[6] && 0x00 == buffer[7] && 0x00 == buffer[8] && 0x00 == buffer[9] && 0x00 == buffer[10] && 0x00 == buffer[11] && 0x00 == buffer[12] ) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_PSD; } //ico //Hex: 00 00 01 00 if (4 <= length && 0x00 == buffer[0] && 0x00 == buffer[1] && 0x01 == buffer[2] && 0x00 == buffer[3]) { return constants.AVS_OFFICESTUDIO_FILE_IMAGE_ICO; } //svg //todo sax parser if (-1 !== startText.indexOf(' { const searchBytes = Buffer.from(filename, 'utf8'); const searchLen = searchBytes.length; const endPos = Math.max(0, length - searchLen); for (let i = endPos; i >= startPos; i--) { let match = true; for (let j = 0; j < searchLen; j++) { if (buffer[i + j] !== searchBytes[j]) { match = false; break; } } if (match) return true; } return false; }; // Check for Excel files if (searchOfficeFile('xl/workbook.xml')) { return constants.AVS_OFFICESTUDIO_FILE_SPREADSHEET_XLSX; } } return constants.AVS_OFFICESTUDIO_FILE_UNKNOWN; } async function getDocumentFormatByFile(file) { const firstBytesLen = 100; let buffer; let fd; try { fd = await open(file, 'r'); const stream = fd.createReadStream({start: 0, end: firstBytesLen}); const chunks = []; for await (const chunk of stream) { chunks.push(Buffer.from(chunk)); } buffer = Buffer.concat(chunks); } finally { await fd?.close(); } return getDocumentFormatBySignature(buffer); } exports.getDocumentFormatBySignature = getDocumentFormatBySignature; exports.getDocumentFormatByFile = getDocumentFormatByFile; exports.getOfficeZipFormatBySignature = getOfficeZipFormatBySignature;