UNPKG

jimu-mobile

Version:

积木组件库助力移动端开发

616 lines (615 loc) 17.7 kB
/* global BinaryAjax */ window.EXIF = (function () { /** * @param {Element} img * @return {?} */ function imageHasData(img) { return !!img.exifdata; } /** * @param {Element} img * @param {Function} callback * @return {undefined} */ function getImageData(img, callback) { BinaryAjax(img.src, (oHTTP) => { const data = findEXIFinJPEG(oHTTP.binaryResponse); img.exifdata = data || {}; if (callback) { callback.call(img); } }); } /** * @param {?} file * @return {?} */ function findEXIFinJPEG(file) { if (file.getByteAt(0) !== 255 || file.getByteAt(1) !== 216) { return false; } let t; /** @type {number} */ let offset = 2; const distanceX = file.getLength(); for (;distanceX > offset;) { if (file.getByteAt(offset) !== 255) { c && console.log(`Not a valid marker at offset ${offset}, found: ${file.getByteAt(offset)}`); return false; } t = file.getByteAt(offset + 1); if (t === 22400) { c && console.log('Found 0xFFE1 marker'); return readEXIFData(file, offset + 4, file.getShortAt(offset + 2, true) - 2); } if (t === 225) { c && console.log('Found 0xFFE1 marker'); return readEXIFData(file, offset + 4, file.getShortAt(offset + 2, true) - 2); } offset += 2 + file.getShortAt(offset + 2, true); } } /** * @param {?} file * @param {?} tiffStart * @param {number} dirStart * @param {?} strings * @param {string} bigEnd * @return {?} */ function readTags(file, tiffStart, dirStart, strings, bigEnd) { let entryOffset; let tag; let high; const val = file.getShortAt(dirStart, bigEnd); const tags = {}; /** @type {number} */ high = 0; for (;val > high; high++) { entryOffset = dirStart + 12 * high + 2; tag = strings[file.getShortAt(entryOffset, bigEnd)]; if (!tag) { if (c) { console.log(`Unknown tag: ${file.getShortAt(entryOffset, bigEnd)}`); } } tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd); } return tags; } /** * @param {?} file * @param {number} entryOffset * @param {?} tiffStart * @param {number} dirStart * @param {undefined} bigEnd * @return {?} */ function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) { let offset; let vals; let val; let n; let numerator; let denominator; const type = file.getShortAt(entryOffset + 2, bigEnd); const numValues = file.getLongAt(entryOffset + 4, bigEnd); const valueOffset = file.getLongAt(entryOffset + 8, bigEnd) + tiffStart; switch (type) { case 1: case 7: if (numValues === 1) { return file.getByteAt(entryOffset + 8, bigEnd); } offset = numValues > 4 ? valueOffset : entryOffset + 8; /** @type {Array} */ vals = []; /** @type {number} */ n = 0; for (;numValues > n; n++) { vals[n] = file.getByteAt(offset + n); } return vals; case 2: offset = numValues > 4 ? valueOffset : entryOffset + 8; return file.getStringAt(offset, numValues - 1); case 3: if (numValues === 1) { return file.getShortAt(entryOffset + 8, bigEnd); } offset = numValues > 2 ? valueOffset : entryOffset + 8; /** @type {Array} */ vals = []; /** @type {number} */ n = 0; for (;numValues > n; n++) { vals[n] = file.getShortAt(offset + 2 * n, bigEnd); } return vals; case 4: if (numValues === 1) { return file.getLongAt(entryOffset + 8, bigEnd); } /** @type {Array} */ vals = []; /** @type {number} */ n = 0; for (;numValues > n; n++) { vals[n] = file.getLongAt(valueOffset + 4 * n, bigEnd); } return vals; case 5: if (numValues === 1) { numerator = file.getLongAt(valueOffset, bigEnd); denominator = file.getLongAt(valueOffset + 4, bigEnd); /* eslint-disable no-new-wrappers */ val = new Number(numerator / denominator); /* eslint-enable no-new-wrappers */ val.numerator = numerator; val.denominator = denominator; return val; } /** @type {Array} */ vals = []; /** @type {number} */ n = 0; for (;numValues > n; n++) { numerator = file.getLongAt(valueOffset + 8 * n, bigEnd); denominator = file.getLongAt(valueOffset + 4 + 8 * n, bigEnd); /* eslint-disable no-new-wrappers */ vals[n] = new Number(numerator / denominator); /* eslint-enable no-new-wrappers */ vals[n].numerator = numerator; vals[n].denominator = denominator; } return vals; case 9: if (numValues === 1) { return file.getSLongAt(entryOffset + 8, bigEnd); } /** @type {Array} */ vals = []; /** @type {number} */ n = 0; for (;numValues > n; n++) { vals[n] = file.getSLongAt(valueOffset + 4 * n, bigEnd); } return vals; case 10: if (numValues === 1) { return file.getSLongAt(valueOffset, bigEnd) / file.getSLongAt(valueOffset + 4, bigEnd); } /** @type {Array} */ vals = []; /** @type {number} */ n = 0; for (;numValues > n; n++) { /** @type {number} */ vals[n] = file.getSLongAt(valueOffset + 8 * n, bigEnd) / file.getSLongAt(valueOffset + 4 + 8 * n, bigEnd); } return vals; } } /** * @param {?} file * @param {number} start * @return {?} */ function readEXIFData(file, start) { if (file.getStringAt(start, 4) !== 'Exif') { c && console.log(`Not valid EXIF data! ${file.getStringAt(start, 4)}`); return false; } let bigEnd; let tags; let tag; let exifData; let gpsData; const tiffOffset = start + 6; if (file.getShortAt(tiffOffset) === 18761) { /** @type {boolean} */ bigEnd = false; } else { if (file.getShortAt(tiffOffset) !== 19789) { c && console.log('Not valid TIFF data! (no 0x4949 or 0x4D4D)'); return false; } /** @type {boolean} */ bigEnd = true; } if (file.getShortAt(tiffOffset + 2, bigEnd) !== 42) { c && console.log('Not valid TIFF data! (no 0x002A)'); return false; } if (file.getLongAt(tiffOffset + 4, bigEnd) !== 8) { c && console.log('Not valid TIFF data! (First offset not 8)', file.getShortAt(tiffOffset + 4, bigEnd)); return false; } tags = readTags(file, tiffOffset, tiffOffset + 8, TiffTags, bigEnd); if (tags.ExifIFDPointer) { exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd); for (tag in exifData) { switch (tag) { case 'LightSource': case 'Flash': case 'MeteringMode': case 'ExposureProgram': case 'SensingMethod': case 'SceneCaptureType': case 'SceneType': case 'CustomRendered': case 'WhiteBalance': case 'GainControl': case 'Contrast': case 'Saturation': case 'Sharpness': case 'SubjectDistanceRange': case 'FileSource': exifData[tag] = StringValues[tag][exifData[tag]]; break; case 'ExifVersion': case 'FlashpixVersion': /** @type {string} */ exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], exifData[tag][3]); break; case 'ComponentsConfiguration': exifData[tag] = StringValues.Components[exifData[tag][0]] + StringValues.Components[exifData[tag][1]] + StringValues.Components[exifData[tag][2]] + StringValues.Components[exifData[tag][3]]; } tags[tag] = exifData[tag]; } } if (tags.GPSInfoIFDPointer) { gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd); for (tag in gpsData) { switch (tag) { case 'GPSVersionID': gpsData[tag] = `${gpsData[tag][0]}.${gpsData[tag][1]}.${gpsData[tag][2]}.${gpsData[tag][3]}`; } tags[tag] = gpsData[tag]; } } return tags; } /** * @param {Element} img * @param {Function} callback * @return {?} */ function getData(img, callback) { return img.complete ? (imageHasData(img) ? callback && callback.call(img) : getImageData(img, callback), true) : false; } /** * @param {Element} img * @param {?} tag * @return {?} */ function getTag(img, tag) { return imageHasData(img) ? img.exifdata[tag] : void 0; } /** * @param {Element} img * @return {?} */ function getAllTags(img) { if (!imageHasData(img)) { return {}; } let prop; const data = img.exifdata; const cache = {}; for (prop in data) { if (data.hasOwnProperty(prop)) { cache[prop] = data[prop]; } } return cache; } /** * @param {Element} img * @return {?} */ function pretty(img) { if (!imageHasData(img)) { return ''; } let a; const data = img.exifdata; /** @type {string} */ let optsData = ''; for (a in data) { if (data.hasOwnProperty(a)) { optsData += typeof data[a] === 'object' ? data[a] instanceof Number ? `${a} : ${data[a]} [${data[a].numerator}/${data[a].denominator}]\r\n` : `${a} : [${data[a].length} values]\r\n` : `${a} : ${data[a]}\r\n`; } } return optsData; } /** * @param {?} file * @return {?} */ function readFromBinaryFile(file) { return findEXIFinJPEG(file); } /** @type {boolean} */ var c = false; var ExifTags = { 36864: 'ExifVersion', 40960: 'FlashpixVersion', 40961: 'ColorSpace', 40962: 'PixelXDimension', 40963: 'PixelYDimension', 37121: 'ComponentsConfiguration', 37122: 'CompressedBitsPerPixel', 37500: 'MakerNote', 37510: 'UserComment', 40964: 'RelatedSoundFile', 36867: 'DateTimeOriginal', 36868: 'DateTimeDigitized', 37520: 'SubsecTime', 37521: 'SubsecTimeOriginal', 37522: 'SubsecTimeDigitized', 33434: 'ExposureTime', 33437: 'FNumber', 34850: 'ExposureProgram', 34852: 'SpectralSensitivity', 34855: 'ISOSpeedRatings', 34856: 'OECF', 37377: 'ShutterSpeedValue', 37378: 'ApertureValue', 37379: 'BrightnessValue', 37380: 'ExposureBias', 37381: 'MaxApertureValue', 37382: 'SubjectDistance', 37383: 'MeteringMode', 37384: 'LightSource', 37385: 'Flash', 37396: 'SubjectArea', 37386: 'FocalLength', 41483: 'FlashEnergy', 41484: 'SpatialFrequencyResponse', 41486: 'FocalPlaneXResolution', 41487: 'FocalPlaneYResolution', 41488: 'FocalPlaneResolutionUnit', 41492: 'SubjectLocation', 41493: 'ExposureIndex', 41495: 'SensingMethod', 41728: 'FileSource', 41729: 'SceneType', 41730: 'CFAPattern', 41985: 'CustomRendered', 41986: 'ExposureMode', 41987: 'WhiteBalance', 41988: 'DigitalZoomRation', 41989: 'FocalLengthIn35mmFilm', 41990: 'SceneCaptureType', 41991: 'GainControl', 41992: 'Contrast', 41993: 'Saturation', 41994: 'Sharpness', 41995: 'DeviceSettingDescription', 41996: 'SubjectDistanceRange', 40965: 'InteroperabilityIFDPointer', 42016: 'ImageUniqueID', }; var TiffTags = { 256: 'ImageWidth', 257: 'ImageHeight', 34665: 'ExifIFDPointer', 34853: 'GPSInfoIFDPointer', 40965: 'InteroperabilityIFDPointer', 258: 'BitsPerSample', 259: 'Compression', 262: 'PhotometricInterpretation', 274: 'Orientation', 277: 'SamplesPerPixel', 284: 'PlanarConfiguration', 530: 'YCbCrSubSampling', 531: 'YCbCrPositioning', 282: 'XResolution', 283: 'YResolution', 296: 'ResolutionUnit', 273: 'StripOffsets', 278: 'RowsPerStrip', 279: 'StripByteCounts', 513: 'JPEGInterchangeFormat', 514: 'JPEGInterchangeFormatLength', 301: 'TransferFunction', 318: 'WhitePoint', 319: 'PrimaryChromaticities', 529: 'YCbCrCoefficients', 532: 'ReferenceBlackWhite', 306: 'DateTime', 270: 'ImageDescription', 271: 'Make', 272: 'Model', 305: 'Software', 315: 'Artist', 33432: 'Copyright', }; var GPSTags = { 0: 'GPSVersionID', 1: 'GPSLatitudeRef', 2: 'GPSLatitude', 3: 'GPSLongitudeRef', 4: 'GPSLongitude', 5: 'GPSAltitudeRef', 6: 'GPSAltitude', 7: 'GPSTimeStamp', 8: 'GPSSatellites', 9: 'GPSStatus', 10: 'GPSMeasureMode', 11: 'GPSDOP', 12: 'GPSSpeedRef', 13: 'GPSSpeed', 14: 'GPSTrackRef', 15: 'GPSTrack', 16: 'GPSImgDirectionRef', 17: 'GPSImgDirection', 18: 'GPSMapDatum', 19: 'GPSDestLatitudeRef', 20: 'GPSDestLatitude', 21: 'GPSDestLongitudeRef', 22: 'GPSDestLongitude', 23: 'GPSDestBearingRef', 24: 'GPSDestBearing', 25: 'GPSDestDistanceRef', 26: 'GPSDestDistance', 27: 'GPSProcessingMethod', 28: 'GPSAreaInformation', 29: 'GPSDateStamp', 30: 'GPSDifferential', }; var StringValues = { ExposureProgram: { 0: 'Not defined', 1: 'Manual', 2: 'Normal program', 3: 'Aperture priority', 4: 'Shutter priority', 5: 'Creative program', 6: 'Action program', 7: 'Portrait mode', 8: 'Landscape mode', }, MeteringMode: { 0: 'Unknown', 1: 'Average', 2: 'CenterWeightedAverage', 3: 'Spot', 4: 'MultiSpot', 5: 'Pattern', 6: 'Partial', 255: 'Other', }, LightSource: { 0: 'Unknown', 1: 'Daylight', 2: 'Fluorescent', 3: 'Tungsten (incandescent light)', 4: 'Flash', 9: 'Fine weather', 10: 'Cloudy weather', 11: 'Shade', 12: 'Daylight fluorescent (D 5700 - 7100K)', 13: 'Day white fluorescent (N 4600 - 5400K)', 14: 'Cool white fluorescent (W 3900 - 4500K)', 15: 'White fluorescent (WW 3200 - 3700K)', 17: 'Standard light A', 18: 'Standard light B', 19: 'Standard light C', 20: 'D55', 21: 'D65', 22: 'D75', 23: 'D50', 24: 'ISO studio tungsten', 255: 'Other', }, Flash: { 0: 'Flash did not fire', 1: 'Flash fired', 5: 'Strobe return light not detected', 7: 'Strobe return light detected', 9: 'Flash fired, compulsory flash mode', 13: 'Flash fired, compulsory flash mode, return light not detected', 15: 'Flash fired, compulsory flash mode, return light detected', 16: 'Flash did not fire, compulsory flash mode', 24: 'Flash did not fire, auto mode', 25: 'Flash fired, auto mode', 29: 'Flash fired, auto mode, return light not detected', 31: 'Flash fired, auto mode, return light detected', 32: 'No flash function', 65: 'Flash fired, red-eye reduction mode', 69: 'Flash fired, red-eye reduction mode, return light not detected', 71: 'Flash fired, red-eye reduction mode, return light detected', 73: 'Flash fired, compulsory flash mode, red-eye reduction mode', 77: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected', 79: 'Flash fired, compulsory flash mode, red-eye reduction mode, return light detected', 89: 'Flash fired, auto mode, red-eye reduction mode', 93: 'Flash fired, auto mode, return light not detected, red-eye reduction mode', 95: 'Flash fired, auto mode, return light detected, red-eye reduction mode', }, SensingMethod: { 1: 'Not defined', 2: 'One-chip color area sensor', 3: 'Two-chip color area sensor', 4: 'Three-chip color area sensor', 5: 'Color sequential area sensor', 7: 'Trilinear sensor', 8: 'Color sequential linear sensor', }, SceneCaptureType: { 0: 'Standard', 1: 'Landscape', 2: 'Portrait', 3: 'Night scene', }, SceneType: { 1: 'Directly photographed', }, CustomRendered: { 0: 'Normal process', 1: 'Custom process', }, WhiteBalance: { 0: 'Auto white balance', 1: 'Manual white balance', }, GainControl: { 0: 'None', 1: 'Low gain up', 2: 'High gain up', 3: 'Low gain down', 4: 'High gain down', }, Contrast: { 0: 'Normal', 1: 'Soft', 2: 'Hard', }, Saturation: { 0: 'Normal', 1: 'Low saturation', 2: 'High saturation', }, Sharpness: { 0: 'Normal', 1: 'Soft', 2: 'Hard', }, SubjectDistanceRange: { 0: 'Unknown', 1: 'Macro', 2: 'Close view', 3: 'Distant view', }, FileSource: { 3: 'DSC', }, Components: { 0: '', 1: 'Y', 2: 'Cb', 3: 'Cr', 4: 'R', 5: 'G', 6: 'B', }, }; return { /** @type {function (?): ?} */ readFromBinaryFile, /** @type {function (Element): ?} */ pretty, /** @type {function (Element, ?): ?} */ getTag, /** @type {function (Element): ?} */ getAllTags, /** @type {function (Element, Function): ?} */ getData, Tags: ExifTags, TiffTags, GPSTags, StringValues, }; }());