UNPKG

image-info-extractor

Version:

A lib to read image info and to extract/parse image metadata

395 lines (393 loc) 13 kB
/** * List of ICC profiel tag names * https://www.color.org/specification/ICC.1-2022-05.pdf */ export const tagSignatureMap = { 'A2B0': 'AToB0', 'A2B1': 'AToB1', 'A2B2': 'AToB2', 'bXYZ': 'blueMatrixColumn', 'bTRC': 'blueTRC', 'B2A0': 'BToA0', 'B2A1': 'BToA1', 'B2A2': 'BToA2', 'B2D0': 'BToD0', 'B2D1': 'BToD1', 'B2D2': 'BToD2', 'B2D3': 'BToD3', 'calt': 'calibrationDateTime', 'targ': 'charTarget', 'chad': 'chromaticAdaptation', 'chrm': 'chromaticity', 'cicp': 'cipc', 'clro': 'colorantOrder', 'clrt': 'colorantTable', 'clot': 'colorantTableOut', 'ciis': 'colorimetricIntentImageState', 'cprt': 'copyright', 'crdi': 'creditInfo', // removed in v4 'data': 'data', // removed in v4 'dtim': 'dateTime', // removed in v4 'dmnd': 'deviceMfgDesc', 'dmdd': 'deviceModelDesc', 'devs': 'deviceSettings', // removed in v4 'D2B0': 'DToB0', 'D2B1': 'DToB1', 'D2B2': 'DToB2', 'D2B3': 'DToB3', 'gamt': 'gamut', 'kTRC': 'grayTRC', 'gXYZ': 'greenMatrixColumn', 'gTRC': 'greenTRC', 'lumi': 'luminance', 'meas': 'measurement', 'meta': 'metadata', 'bkpt': 'mediaBlackPoint', // removed in v4.4 'wtpt': 'mediaWhitePoint', 'ncol': 'namedColor', // obselete, use ncl2 'ncl2': 'namedColor2', 'resp': 'outputResponse', 'rig0': 'perceptualRenderingIntentGamut', 'pre0': 'preview0', 'pre1': 'preview1', 'pre2': 'preview2', 'desc': 'profileDescription', 'pseq': 'profileSequenceDesc', 'psd0': 'ps2CRD0', // removed in v4 'psd1': 'ps2CRD1', // removed in v4 'psd2': 'ps2CRD2', // removed in v4 'psd3': 'ps2CRD3', // removed in v4 'ps2s': 'ps2CSA', // removed in v4.4 'ps2i': 'ps2RenderingIntent', // removed in v4.4 'rXYZ': 'redMatrixColumn', 'rTRC': 'redTRC', 'rig2': 'saturationRenderingIntentGamut', 'scrd': 'screeningDesc', // removed in v4 'scrn': 'screening', // removed in v4 'tech': 'technology', 'bfd ': 'ucrBg', // removed in v4 'vued': 'viewingCondDesc', 'view': 'viewingConditions', }; export let tagParsers = { // ====== v2 ====== // 6.5.17 'desc': function textDescription(view, offset, length) { let asciiLength = view.getUint32(offset + 8); let description = view.getBinaryString(offset + 12, asciiLength, true); let j = offset + 12 + asciiLength; let _languageCode = view.getUint32(j); let unicodeLength = view.getUint32(j + 4); let unicodeDescription = view.getUTF16BEString(j + 8, unicodeLength, true); j += 4 + unicodeLength; let _scriptCode = view.getUint16(j); let localizedLength = view.getUint8(j + 2); let localizedDescription = view.getBinaryString(j + 3, localizedLength, true); // return { // description, // unicodeDescription, // localizedDescription, // languageCode, // scriptCode, // }; return description || unicodeDescription || localizedDescription; }, // ====== v4 ====== // 10.2 'chrm': function chromaticity(view, offset, length) { let channelCount = view.getUint16(offset + 8); let channels = []; for (let i = 0, j = offset + 12; i < channelCount; i++, j += 8) { let a = new Float64Array(2); a[0] = view.getU16Fixed16Number(j); a[1] = view.getU16Fixed16Number(j + 4); channels.push(a); } return { colorantType: view.getUint16(offset + 10), channels, }; }, // 10.3 'cipc': function cipc(view, offset, length) { return { colorPrimaries: view.getUint8(offset + 8), transferCharacteristics: view.getUint8(offset + 8), matrixCoefficients: view.getUint8(offset + 10), videoFullRangeFlag: view.getUint8(offset + 11), }; }, // 10.4 'clro': function colorantOrder(view, offset, length) { let count = view.getUint32(offset + 8); let order = new Uint8Array(count); for (let i = 0, j = offset + 11; i < count; i++, j++) { order[i] = view.getUint8(j); } return order; }, // 10.5 'clrt': function colorantTable(view, offset, length) { let colorantCount = view.getUint32(offset + 8); let colorants = []; for (let i = 0, j = offset + 12; i < colorantCount; i++, j += 38) { let name = view.getBinaryString(j, 32, true); let pcsValues = new Uint16Array(3); pcsValues[0] = view.getUint16(j + 32); pcsValues[1] = view.getUint16(j + 34); pcsValues[2] = view.getUint16(j + 36); colorants.push({colorantName: name, pcsValues}); } return colorants; }, // 10.6 'curv': function colorantTable(view, offset, length) { let count = view.getUint32(offset + 8); let array = new Uint16Array(count); for (let i = 0, j = offset + 12; i < count; i++, j += 2) { array[i] = view.getUint16(j); } return array; }, // 10.7 'data': function data(view, offset, length) { let dataType = view.getUint32(offset + 8); if (dataType === 0) { // ascii text return view.getBinaryString(offset + 12, length - 12); } return new Uint8Array(view.buffer, view.byteOffset, offset + 12, length - 12); }, // 10.8 'dtim': function dateTime(view, offset, length) { return view.getDateTimeNumber(offset + 8); }, // 10.9 'dict': function dict(view, offset, length) { let count = view.getDateTimeNumber(offset + 8); let dict = []; let recordSize = view.getUint32(offset + 12); for (let i = 0, j = offset + 16; i < count; i++, j += recordSize) { let namePos = view.getPositionNumber(j); let valuePos = view.getPositionNumber(j + 8); let name = view.getUTF16BEString(offset + namePos.offset, namePos.length); let value = view.getUTF16BEString(offset + valuePos.offset, valuePos.length); let entry = {name, value}; if (recordSize >= 24) { let displayNamePos = view.getPositionNumber(j + 16); entry.displayName = view.getUTF16BEString(offset + displayNamePos.offset, displayNamePos.length); if (recordSize >= 32) { let displayValuePos = view.getPositionNumber(j + 24); entry.displayValue = view.getUTF16BEString(offset + displayValuePos.offset, displayValuePos.length); } } dict.push(entry); } return dict; }, // 10.10 'mft2': function lut16(view, offset, length) { let numInputChannels = view.getUint8(offset + 8); let numOutputChannels = view.getUint8(offset + 9); let numGridPoints = view.getUint8(offset + 9); let parameters = new Float64Array(9); let j = offset + 12; for (let i = 0; i < 9; i++, j += 2) { parameters[i] = view.getS15Fixed16Number(j); } let numInputEntries = view.getUint16(offset + 48); let numOutputEntries = view.getUint16(offset + 50); let inputLength = numInputChannels * numInputEntries; let inputTables = new Uint16Array(inputLength); j = offset + 52; for (let i = 0; i < inputLength; i++, j += 2) { inputTables[i] = view.getUint16(j); } let clutLength = Math.pow(numGridPoints, numInputChannels) * numOutputChannels; let clutTables = new Uint16Array(clutLength); for (let i = 0; i < clutLength; i++, j += 2) { clutTables[i] = view.getUint16(j); } let outputLength = numOutputChannels * numOutputEntries; let outputTables = new Uint16Array(outputLength); for (let i = 0; i < outputLength; i++, j += 2) { outputTables[i] = view.getUint16(j); } return { numInputChannels, numOutputChannels, numGridPoints, numInputEntries, numOutputEntries, inputTables, clutTables, outputTables, }; }, // 10.11 'mft1': function lut16(view, offset, length) { let numInputChannels = view.getUint8(offset + 8); let numOutputChannels = view.getUint8(offset + 9); let numGridPoints = view.getUint8(offset + 9); let parameters = new Float64Array(9); let j = offset + 12; for (let i = 0; i < 9; i++, j += 2) { parameters[i] = view.getS15Fixed16Number(j); } let numInputEntries = 256; let numOutputEntries = 256; let inputLength = numInputChannels * numInputEntries; let inputTables = new Uint8Array(inputLength); j = offset + 52; for (let i = 0; i < inputLength; i++, j++) { inputTables[i] = view.getUint8(j); } let clutLength = Math.pow(numGridPoints, numInputChannels) * numOutputChannels; let clutTables = new Uint8Array(clutLength); for (let i = 0; i < clutLength; i++, j++) { clutTables[i] = view.getUint8(j); } let outputLength = numOutputChannels * numOutputEntries; let outputTables = new Uint8Array(outputLength); for (let i = 0; i < outputLength; i++, j++) { outputTables[i] = view.getUint8(j); } return { numInputChannels, numOutputChannels, numGridPoints, numInputEntries, numOutputEntries, inputTables, clutTables, outputTables, }; }, // 10.12 'mAB ': null, // lutAToB // 10.13 'mBA ': null, // lutBToA // 10.14 'meas': function measurement(view, offset, length) { return { standardObserver: view.getUint32(offset + 8), nCIEXYZ: view.getXYZNumber(offset + 12), measurementGeometry: view.getUint32(offset + 24), measurementFlare: view.getUint32(offset + 28), standardIlluminant: view.getUint32(offset + 32), }; }, // 10.15 'mluc': function multiLocalizedUnicode(view, offset, length) { let numRecords = view.getUint32(offset + 8); let recordSize = view.getUint32(offset + 12); if (recordSize !== 12) { throw new Error('Invalid mluc record size: ' + recordSize); } let records = []; for (let i = 0, j = offset + 16; i < numRecords; i++, j += 12) { let language = view.getBinaryString(j, 2, true); let country = view.getBinaryString(j + 2, 2, true); let textPos = {length: view.getUint32(j + 4), offset: view.getUint32(j + 8)}; let locale = country ? language + '-' + country : language; records.push({ locale, text: view.getUTF16BEString(offset + textPos.offset, textPos.length, true), }); } return records[0].text; }, // 10.16 'mpet': null, // multiProcessElements // 10.17 'ncl2': null, // namedColor2 // 10.18 'para': function parametricCurve(view, offset, length) { let functionType = view.getUint16(offset + 8); let count = (length - 12) / 4; let parameters = new Float64Array(count); for (let i = 0, j = offset + 12; i < count; i++, j += 4) { parameters[i] = view.getS15Fixed16Number(offset + 12); } return {functionType, parameters}; }, // 10.19 'pseq': null, // profileSequenceDesc // 10.20 'psid': null, // profileSequenceIdentifier // 10.21 'rcs2': null, // responseCurveSet16 // 10.22 'sf32': function s15Fixed16Array(view, offset, length) { let count = (length - 8) / 4; let array = new Float64Array(count); for (let i = 0, j = offset + 8; i < count; i++, j += 4) { array[i] = view.getS15Fixed16Number(j); } return array; }, // 10.23 'sig ': function signature(view, offset, length) { return view.getBinaryString4(offset + 8); }, // 10.24 'text': function text(view, offset, length) { return view.getBinaryString(offset + 8, length - 8, true); }, // 10.25 'uf32': function u16Fixed16Array(view, offset, length) { let count = (length - 8) / 4; let array = new Float64Array(count); for (let i = 0, j = offset + 8; i < count; i++, j += 4) { array[i] = view.getU16Fixed16Number(j); } return array; }, // 10.26 'ui16': function uInt16Array(view, offset, length) { let count = (length - 8) / 2; let array = new Uint16Array(count); for (let i = 0, j = offset + 8; i < count; i++, j += 2) { array[i] = view.getUint16(j); } return array; }, // 10.27 'ui32': function uInt32Array(view, offset, length) { let count = (length - 8) / 4; let array = new Uint32Array(count); for (let i = 0, j = offset + 8; i < count; i++, j += 4) { array[i] = view.getUint32(j); } return array; }, // 10.28 'ui64': function uInt64Array(view, offset, length) { let count = (length - 8) / 8; let array = new BigUint64Array(count); for (let i = 0, j = offset + 8; i < count; i++, j += 8) { array[i] = view.getBigUint64(j); } return array; }, // 10.29 'ui08': function uInt8Array(view, offset, length) { let count = (length - 8); let array = new Uint8Array(count); for (let i = 0, j = offset + 8; i < count; i++, j += 1) { array[i] = view.getUint8(j); } return array; }, // 10.30 'view': function viewingConditions(view, offset, length) { return { illuminant: view.getXYZNumber(offset + 8), surround: view.getXYZNumber(offset + 10), illuminantType: tagParsers['meas'](view, offset + 32, length - 32) }; }, // 10.31 'XYZ ': function viewingConditions(view, offset, length) { return view.getXYZNumber(offset + 8); }, };