@eris/exif
Advanced tools
Comparing version 0.4.2-alpha.26 to 0.4.2-alpha.27
@@ -30,2 +30,10 @@ "use strict"; | ||
const log = log_1.createLogger('decoder'); | ||
function sequenceMatch(largeArray, smallArray, startIndex) { | ||
for (let i = 0; i < smallArray.length; i++) { | ||
const iLarge = i + startIndex; | ||
if (largeArray[iLarge] !== smallArray[i]) | ||
return false; | ||
} | ||
return true; | ||
} | ||
class TIFFDecoder { | ||
@@ -220,2 +228,6 @@ constructor(buffer) { | ||
return; | ||
// See https://github.com/exiftool/exiftool/blob/a7f6bc1e03ff3553da66aed2b552d2e2f64b71b3/lib/Image/ExifTool/Olympus.pm#L1763-L1779 | ||
const PREVIEW_IMAGE_OFFSET_SEQUENCE = [0x01, 0x01, 0x04, 0x00, 0x01, 0x00]; | ||
const PREVIEW_IMAGE_LENGTH_SEQUENCE = [0x02, 0x01, 0x04, 0x00, 0x01, 0x00]; | ||
const JPEG_SEQUENCE = [0xff, 0xd8, 0xff]; | ||
const ifdEntries = this.extractIFDEntries(); | ||
@@ -225,6 +237,32 @@ const makerNoteEntry = ifdEntries.find(entry => entry.tag === 37500); | ||
return; | ||
const makernoteBuffer = makerNoteEntry.getValue(this._reader); | ||
if (typeof makernoteBuffer === 'string' || typeof makernoteBuffer === 'number') | ||
const makernote = makerNoteEntry.getValue(this._reader); | ||
if (typeof makernote === 'string' || typeof makernote === 'number') | ||
return; | ||
return this._findJPEGInRange(makernoteBuffer, 0, makernoteBuffer.length); | ||
const makernoteReader = new reader_1.Reader(makernote); | ||
makernoteReader.setEndianess(types_1.Endian.Little); | ||
for (let i = 0; i < makernote.length; i++) { | ||
// Olympus makernote has really fucked up IFD offsets. | ||
// So we just do a linear scan for the two tags we are looking for | ||
if (!sequenceMatch(makernote, PREVIEW_IMAGE_OFFSET_SEQUENCE, i)) | ||
continue; | ||
if (!sequenceMatch(makernote, PREVIEW_IMAGE_LENGTH_SEQUENCE, i + 12)) | ||
continue; | ||
makernoteReader.seek(i); | ||
const offsetEntry = ifd_entry_1.IFDEntry.read(makernoteReader); | ||
const offset = offsetEntry.getValue(makernoteReader); | ||
if (typeof offset !== 'number') | ||
continue; | ||
if (!sequenceMatch(makernote, JPEG_SEQUENCE, offset)) | ||
continue; | ||
makernoteReader.seek(i + 12); | ||
const lengthEntry = ifd_entry_1.IFDEntry.read(makernoteReader); | ||
const length = lengthEntry.getValue(makernoteReader); | ||
if (typeof length !== 'number') | ||
continue; | ||
const jpegBuffer = makernote.slice(offset, offset + length); | ||
if (!jpeg_decoder_1.JPEGDecoder.isJPEG(jpegBuffer)) | ||
continue; | ||
return jpegBuffer; | ||
} | ||
return this._findJPEGInRange(makernote, 0, makernote.length); | ||
} | ||
@@ -231,0 +269,0 @@ _readLargestJPEG() { |
@@ -8,3 +8,3 @@ "use strict"; | ||
const log = log_1.createLogger('encoder'); | ||
const BLACKLISTED_TAGS = new Set([ | ||
const DISALLOWED_TAGS = new Set([ | ||
types_1.IFDTag.SubIFD, | ||
@@ -18,2 +18,26 @@ types_1.IFDTag.EXIFOffset, | ||
]); | ||
const ALLOWLIST_TAGS = new Set([ | ||
// These are what we really need | ||
tags_1.tags.ImageWidth.code, | ||
tags_1.tags.ImageLength.code, | ||
tags_1.tags.Orientation.code, | ||
tags_1.tags.ISO.code, | ||
// These were added for backcompat, but might not actually work. | ||
tags_1.tags.Compression.code, | ||
tags_1.tags.ResolutionUnit.code, | ||
tags_1.tags.PhotometricInterpretation.code, | ||
tags_1.tags.SamplesPerPixel.code, | ||
tags_1.tags.PlanarConfiguration.code, | ||
tags_1.tags.MeteringMode.code, | ||
tags_1.tags.YCbCrPositioning.code, | ||
tags_1.tags.BitsPerSample.code, | ||
tags_1.tags.NewSubfileType.code, | ||
tags_1.tags.AsShotNeutral.code, | ||
tags_1.tags.CalibrationIlluminant1.code, | ||
tags_1.tags.CalibrationIlluminant2.code, | ||
tags_1.tags.WhiteLevel.code, | ||
tags_1.tags.TileWidth.code, | ||
tags_1.tags.TileLength.code, | ||
tags_1.tags.GPSTag.code, | ||
]); | ||
class TIFFEncoder { | ||
@@ -25,4 +49,8 @@ static isSupportedEntry(tag, value) { | ||
return false; | ||
if (BLACKLISTED_TAGS.has(tag.code)) | ||
if (DISALLOWED_TAGS.has(tag.code)) | ||
return false; | ||
if (!ALLOWLIST_TAGS.has(tag.code)) | ||
return false; | ||
if (typeof value !== 'number') | ||
return false; | ||
if (tag.dataType === types_1.IFDDataType.Short) | ||
@@ -29,0 +57,0 @@ return value < Math.pow(2, 16); |
@@ -79,3 +79,5 @@ /// <reference types="node" /> | ||
EXIFOffset = 34665, | ||
PanasonicJPEGEnd = 280 | ||
PanasonicJPEGEnd = 280, | ||
ISO = 34855, | ||
MakerNote = 37500 | ||
} | ||
@@ -82,0 +84,0 @@ export declare enum IFDGroup { |
@@ -25,2 +25,4 @@ "use strict"; | ||
IFDTag[IFDTag["PanasonicJPEGEnd"] = 280] = "PanasonicJPEGEnd"; | ||
IFDTag[IFDTag["ISO"] = 34855] = "ISO"; | ||
IFDTag[IFDTag["MakerNote"] = 37500] = "MakerNote"; | ||
})(IFDTag = exports.IFDTag || (exports.IFDTag = {})); | ||
@@ -27,0 +29,0 @@ var IFDGroup; |
@@ -50,2 +50,15 @@ import {IFD} from '../decoder/ifd' | ||
function sequenceMatch( | ||
largeArray: number[] | Uint8Array, | ||
smallArray: number[] | Uint8Array, | ||
startIndex: number, | ||
): boolean { | ||
for (let i = 0; i < smallArray.length; i++) { | ||
const iLarge = i + startIndex | ||
if (largeArray[iLarge] !== smallArray[i]) return false | ||
} | ||
return true | ||
} | ||
export class TIFFDecoder { | ||
@@ -262,2 +275,7 @@ private readonly _buffer: IBufferLike | ||
// See https://github.com/exiftool/exiftool/blob/a7f6bc1e03ff3553da66aed2b552d2e2f64b71b3/lib/Image/ExifTool/Olympus.pm#L1763-L1779 | ||
const PREVIEW_IMAGE_OFFSET_SEQUENCE = [0x01, 0x01, 0x04, 0x00, 0x01, 0x00] | ||
const PREVIEW_IMAGE_LENGTH_SEQUENCE = [0x02, 0x01, 0x04, 0x00, 0x01, 0x00] | ||
const JPEG_SEQUENCE = [0xff, 0xd8, 0xff] | ||
const ifdEntries = this.extractIFDEntries() | ||
@@ -267,5 +285,31 @@ const makerNoteEntry = ifdEntries.find(entry => entry.tag === 37500) | ||
const makernoteBuffer = makerNoteEntry.getValue(this._reader) | ||
if (typeof makernoteBuffer === 'string' || typeof makernoteBuffer === 'number') return | ||
return this._findJPEGInRange(makernoteBuffer, 0, makernoteBuffer.length) | ||
const makernote = makerNoteEntry.getValue(this._reader) | ||
if (typeof makernote === 'string' || typeof makernote === 'number') return | ||
const makernoteReader = new Reader(makernote) | ||
makernoteReader.setEndianess(Endian.Little) | ||
for (let i = 0; i < makernote.length; i++) { | ||
// Olympus makernote has really fucked up IFD offsets. | ||
// So we just do a linear scan for the two tags we are looking for | ||
if (!sequenceMatch(makernote, PREVIEW_IMAGE_OFFSET_SEQUENCE, i)) continue | ||
if (!sequenceMatch(makernote, PREVIEW_IMAGE_LENGTH_SEQUENCE, i + 12)) continue | ||
makernoteReader.seek(i) | ||
const offsetEntry = IFDEntry.read(makernoteReader) | ||
const offset = offsetEntry.getValue(makernoteReader) | ||
if (typeof offset !== 'number') continue | ||
if (!sequenceMatch(makernote, JPEG_SEQUENCE, offset)) continue | ||
makernoteReader.seek(i + 12) | ||
const lengthEntry = IFDEntry.read(makernoteReader) | ||
const length = lengthEntry.getValue(makernoteReader) | ||
if (typeof length !== 'number') continue | ||
const jpegBuffer = makernote.slice(offset, offset + length) | ||
if (!JPEGDecoder.isJPEG(jpegBuffer)) continue | ||
return jpegBuffer | ||
} | ||
return this._findJPEGInRange(makernote, 0, makernote.length) | ||
} | ||
@@ -272,0 +316,0 @@ |
@@ -18,3 +18,3 @@ import { | ||
const BLACKLISTED_TAGS = new Set([ | ||
const DISALLOWED_TAGS = new Set([ | ||
IFDTag.SubIFD, | ||
@@ -29,2 +29,28 @@ IFDTag.EXIFOffset, | ||
const ALLOWLIST_TAGS = new Set([ | ||
// These are what we really need | ||
tags.ImageWidth.code, | ||
tags.ImageLength.code, | ||
tags.Orientation.code, | ||
tags.ISO.code, | ||
// These were added for backcompat, but might not actually work. | ||
tags.Compression.code, | ||
tags.ResolutionUnit.code, | ||
tags.PhotometricInterpretation.code, | ||
tags.SamplesPerPixel.code, | ||
tags.PlanarConfiguration.code, | ||
tags.MeteringMode.code, | ||
tags.YCbCrPositioning.code, | ||
tags.BitsPerSample.code, | ||
tags.NewSubfileType.code, | ||
tags.AsShotNeutral.code, | ||
tags.CalibrationIlluminant1.code, | ||
tags.CalibrationIlluminant2.code, | ||
tags.WhiteLevel.code, | ||
tags.TileWidth.code, | ||
tags.TileLength.code, | ||
tags.GPSTag.code, | ||
]) | ||
export class TIFFEncoder { | ||
@@ -34,3 +60,5 @@ public static isSupportedEntry(tag: IIFDTagDefinition | undefined, value: any): boolean { | ||
if (tag.group !== IFDGroup.EXIF) return false | ||
if (BLACKLISTED_TAGS.has(tag.code)) return false | ||
if (DISALLOWED_TAGS.has(tag.code)) return false | ||
if (!ALLOWLIST_TAGS.has(tag.code)) return false | ||
if (typeof value !== 'number') return false | ||
if (tag.dataType === IFDDataType.Short) return value < Math.pow(2, 16) | ||
@@ -37,0 +65,0 @@ if (tag.dataType === IFDDataType.Long) return value < Math.pow(2, 32) |
@@ -91,2 +91,4 @@ export interface ILogger { | ||
PanasonicJPEGEnd = 280, // MinSampleValue normally, marks the start of the RAW data | ||
ISO = 34855, | ||
MakerNote = 37500, | ||
} | ||
@@ -93,0 +95,0 @@ |
{ | ||
"name": "@eris/exif", | ||
"version": "0.4.2-alpha.26", | ||
"version": "0.4.2-alpha.27", | ||
"description": "Parses EXIF data.", | ||
@@ -41,3 +41,3 @@ "main": "./dist/index.js", | ||
}, | ||
"gitHead": "ea3b937e8d9af3b7c49752a84e70e34aca15093d" | ||
"gitHead": "1a02ab5341f82ab094a488235c85206630d38cd8" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
292710
5301