exif
Advanced tools
Comparing version 0.1.0 to 0.2.0
@@ -7,27 +7,27 @@ // | ||
return this[offset]; | ||
} | ||
}; | ||
Buffer.prototype.getSignedByte = function (offset) { | ||
return (this[offset] > 127) ? this[offset] - 256 : this[offset]; | ||
} | ||
}; | ||
Buffer.prototype.getShort = function (offset, bigEndian) { | ||
var short = (bigEndian) ? (this[offset] << 8) + this[offset + 1] : (this[offset + 1] << 8) + this[offset]; | ||
return (short < 0) ? short + 65536 : short; | ||
} | ||
var shortVal = (bigEndian) ? (this[offset] << 8) + this[offset + 1] : (this[offset + 1] << 8) + this[offset]; | ||
return (shortVal < 0) ? shortVal + 65536 : shortVal; | ||
}; | ||
Buffer.prototype.getSignedShort = function (offset, bigEndian) { | ||
var short = (bigEndian) ? (this[offset] << 8) + this[offset + 1] : (this[offset + 1] << 8) + this[offset]; | ||
return (short > 32767) ? short - 65536 : short; | ||
} | ||
var shortVal = (bigEndian) ? (this[offset] << 8) + this[offset + 1] : (this[offset + 1] << 8) + this[offset]; | ||
return (shortVal > 32767) ? shortVal - 65536 : shortVal; | ||
}; | ||
Buffer.prototype.getLong = function (offset, bigEndian) { | ||
var long = (bigEndian) ? (((((this[offset] << 8) + this[offset + 1]) << 8) + this[offset + 2]) << 8) + this[offset + 3] : (((((this[offset + 3] << 8) + this[offset + 2]) << 8) + this[offset + 1]) << 8) + this[offset]; | ||
return (long < 0) ? long + 4294967296 : long; | ||
} | ||
var longVal = (bigEndian) ? (((((this[offset] << 8) + this[offset + 1]) << 8) + this[offset + 2]) << 8) + this[offset + 3] : (((((this[offset + 3] << 8) + this[offset + 2]) << 8) + this[offset + 1]) << 8) + this[offset]; | ||
return (longVal < 0) ? longVal + 4294967296 : longVal; | ||
}; | ||
Buffer.prototype.getSignedLong = function (offset, bigEndian) { | ||
var long = (bigEndian) ? (((((this[offset] << 8) + this[offset + 1]) << 8) + this[offset + 2]) << 8) + this[offset + 3] : (((((this[offset + 3] << 8) + this[offset + 2]) << 8) + this[offset + 1]) << 8) + this[offset]; | ||
return (long > 2147483647) ? long - 4294967296 : long; | ||
} | ||
var longVal = (bigEndian) ? (((((this[offset] << 8) + this[offset + 1]) << 8) + this[offset + 2]) << 8) + this[offset + 3] : (((((this[offset + 3] << 8) + this[offset + 2]) << 8) + this[offset + 1]) << 8) + this[offset]; | ||
return (longVal > 2147483647) ? longVal - 4294967296 : longVal; | ||
}; | ||
@@ -39,2 +39,2 @@ Buffer.prototype.getString = function (offset, length) { | ||
return string.join(''); | ||
} | ||
}; |
@@ -31,7 +31,7 @@ var fs = require('fs'), | ||
image : [], // Information about the main image | ||
thumbnail : [], // Information about the thumbnail | ||
thumbnail : [], // Information about the thumbnail | ||
exif : [], // Exif information | ||
gps : [], // GPS information | ||
interoperability: [], // Exif Interoperability information | ||
makernote : null // Makernote information (n/a yet) | ||
gps : [], // GPS information | ||
interoperability: [], // Exif Interoperability information | ||
makernote : [] // Makernote information | ||
}; | ||
@@ -66,5 +66,5 @@ | ||
rawData = fs.readFile(image, function (error, data) { | ||
fs.readFile(image, function (error, data) { | ||
if (error) | ||
callback({ message : 'Encountered the following error while trying to read given image: '+error }); | ||
callback(new Error('Encountered the following error while trying to read given image: '+error)); | ||
else | ||
@@ -76,7 +76,7 @@ self.processImage(data, callback); | ||
callback({ message : 'Given image is neither a buffer nor a file, please provide one of these.' }); | ||
callback(new Error('Given image is neither a buffer nor a file, please provide one of these.')); | ||
} | ||
} | ||
}; | ||
@@ -91,27 +91,34 @@ ExifImage.prototype.processImage = function (data, callback) { | ||
} else { | ||
callback({ message : 'The given image is not a JPEG and thus unsupported right now.' }); | ||
callback(new Error('The given image is not a JPEG and thus unsupported right now.')); | ||
return; | ||
} | ||
try { | ||
while (offset < data.length) { | ||
if (data[offset++] != 0xFF) { | ||
callback({ message : 'Invalid marker found at offset '+(--offset)+'. Expected 0xFF but found 0x'+data[offset].toString(16).toUpperCase() }); | ||
return; | ||
while (offset < data.length) { | ||
if (data[offset++] != 0xFF) { | ||
callback(new Error('Invalid marker found at offset '+(--offset)+'. Expected 0xFF but found 0x'+data[offset].toString(16).toUpperCase()+".")); | ||
return; | ||
} | ||
if (data[offset++] == 0xE1) { | ||
var exifData = self.extractExifData(data, offset + 2, data.getShort(offset, true) - 2); | ||
callback(false, exifData); | ||
return; | ||
} else { | ||
offset += data.getShort(offset, true); | ||
} | ||
} | ||
if (data[offset++] == 0xE1) { | ||
self.extractExifData(data, offset + 2, data.getShort(offset, true) - 2, callback); | ||
return; | ||
} else { | ||
offset += data.getShort(offset, true); | ||
} | ||
} catch (error) { | ||
callback(error); | ||
} | ||
callback({ message : 'No Exif segment found in the given image' }); | ||
callback(new Error('No Exif segment found in the given image.')); | ||
} | ||
}; | ||
ExifImage.prototype.extractExifData = function (data, start, length, callback) { | ||
ExifImage.prototype.extractExifData = function (data, start, length) { | ||
@@ -124,4 +131,3 @@ var self = this; | ||
if (data.toString('utf8', start, tiffOffset) != 'Exif\0\0') { | ||
callback({ message : 'The Exif data ist not valid.' }); | ||
return; | ||
throw new Error('The Exif data ist not valid.'); | ||
} | ||
@@ -136,17 +142,15 @@ | ||
} else { | ||
callback({ message : 'Invalid TIFF data! Expected 0x4949 or 0x4D4D at offset '+(tiffOffset)+' but found 0x'+data[tiffOffset].toString(16).toUpperCase()+data[tiffOffset + 1].toString(16).toUpperCase() }); | ||
return; | ||
throw new Error('Invalid TIFF data! Expected 0x4949 or 0x4D4D at offset '+(tiffOffset)+' but found 0x'+data[tiffOffset].toString(16).toUpperCase()+data[tiffOffset + 1].toString(16).toUpperCase()+"."); | ||
} | ||
// Valid TIFF headers always have 0x002A here | ||
if (data.getShort(tiffOffset + 2, this.isBigEndian) != 0x002A) { | ||
var expected = (this.isBigEndian) ? '0x002A' : '0x2A00'; | ||
callback({ message : 'Invalid TIFF data! Expected '+expected+' at offset '+(tiffOffset + 2)+' but found 0x'+data[tiffOffset + 2].toString(16).toUpperCase()+data[tiffOffset + 3].toString(16).toUpperCase() }); | ||
return; | ||
} | ||
if (data.getShort(tiffOffset + 2, this.isBigEndian) != 0x002A) { | ||
var expected = (this.isBigEndian) ? '0x002A' : '0x2A00'; | ||
throw new Error('Invalid TIFF data! Expected '+expected+' at offset '+(tiffOffset + 2)+' but found 0x'+data[tiffOffset + 2].toString(16).toUpperCase()+data[tiffOffset + 3].toString(16).toUpperCase()+"."); | ||
} | ||
/********************************* IFD0 **********************************/ | ||
/********************************* IFD0 **********************************/ | ||
// Offset to IFD0 which is always followed by two bytes with the amount of | ||
// entries in this IFD | ||
// Offset to IFD0 which is always followed by two bytes with the amount of | ||
// entries in this IFD | ||
ifdOffset = tiffOffset + data.getLong(tiffOffset + 4, this.isBigEndian); | ||
@@ -158,3 +162,3 @@ numberOfEntries = data.getShort(ifdOffset, this.isBigEndian); | ||
for (var i = 0; i < numberOfEntries; i++) { | ||
var exifEntry = self.extractExifEntry(data, (ifdOffset + 2 + (i * 12)), tiffOffset, ExifImage.TAGS.tiff); | ||
var exifEntry = self.extractExifEntry(data, (ifdOffset + 2 + (i * 12)), tiffOffset, this.isBigEndian, ExifImage.TAGS.tiff); | ||
if (exifEntry) this.exifData.image.push(exifEntry); | ||
@@ -165,7 +169,7 @@ } | ||
// Check if there is an offset for IFD1. If so it is always followed by two | ||
// Check if there is an offset for IFD1. If so it is always followed by two | ||
// bytes with the amount of entries in this IFD, if not there is no IFD1 | ||
ifdOffset = tiffOffset + data.getLong(ifdOffset + 2 + (numberOfEntries * 12)); | ||
ifdOffset = tiffOffset + data.getLong(ifdOffset + 2 + (numberOfEntries * 12), this.isBigEndian); | ||
if (ifdOffset != 0x00000000) { | ||
numberOfEntries = data.getShort(ifdOffset, true); | ||
numberOfEntries = data.getShort(ifdOffset, this.isBigEndian); | ||
@@ -175,3 +179,3 @@ // Each IFD entry consists of 12 bytes which we loop through and extract | ||
for (var i = 0; i < numberOfEntries; i++) { | ||
var exifEntry = self.extractExifEntry(data, (ifdOffset + 2 + (i * 12)), tiffOffset, ExifImage.TAGS.tiff); | ||
var exifEntry = self.extractExifEntry(data, (ifdOffset + 2 + (i * 12)), tiffOffset, this.isBigEndian, ExifImage.TAGS.tiff); | ||
if (exifEntry) this.exifData.thumbnail.push(exifEntry); | ||
@@ -193,3 +197,3 @@ } | ||
for (var i = 0; i < numberOfEntries; i++) { | ||
var exifEntry = self.extractExifEntry(data, (ifdOffset + 2 + (i * 12)), tiffOffset, ExifImage.TAGS.exif); | ||
var exifEntry = self.extractExifEntry(data, (ifdOffset + 2 + (i * 12)), tiffOffset, this.isBigEndian, ExifImage.TAGS.exif); | ||
if (exifEntry) this.exifData.exif.push(exifEntry); | ||
@@ -214,3 +218,3 @@ } | ||
for (var i = 0; i < numberOfEntries; i++) { | ||
var exifEntry = self.extractExifEntry(data, (ifdOffset + 2 + (i * 12)), tiffOffset, ExifImage.TAGS.gps); | ||
var exifEntry = self.extractExifEntry(data, (ifdOffset + 2 + (i * 12)), tiffOffset, this.isBigEndian, ExifImage.TAGS.gps); | ||
if (exifEntry) this.exifData.gps.push(exifEntry); | ||
@@ -235,3 +239,3 @@ } | ||
for (var i = 0; i < numberOfEntries; i++) { | ||
var exifEntry = self.extractExifEntry(data, (ifdOffset + 2 + (i * 12)), tiffOffset); | ||
var exifEntry = self.extractExifEntry(data, (ifdOffset + 2 + (i * 12)), tiffOffset, this.isBigEndian); | ||
if (exifEntry) this.exifData.interoperability.push(exifEntry); | ||
@@ -246,18 +250,45 @@ } | ||
// Nothing here, yet | ||
// Look for Makernote data in the Exif IFD, check which type of proprietary | ||
// Makernotes the image contains, load the respective functionality and | ||
// start the extraction | ||
for (exifEntry in this.exifData.exif) { | ||
if (this.exifData.exif[exifEntry].tag.getShort(0, this.isBigEndian) == 0x927C) { | ||
// Check the header to see what kind of Makernote we are dealing with | ||
if (this.exifData.exif[exifEntry].value.getString(0, 7) === "OLYMP\x00\x01") { | ||
this.extractMakernotes = require('./makernotes/olympus').extractMakernotes; | ||
} else if (this.exifData.exif[exifEntry].value.getString(0, 7) === "AGFA \x00\x01") { | ||
this.extractMakernotes = require('./makernotes/agfa').extractMakernotes; | ||
} else if (this.exifData.exif[exifEntry].value.getString(0, 8) === "EPSON\x00\x01\x00") { | ||
this.extractMakernotes = require('./makernotes/epson').extractMakernotes; | ||
} else if (this.exifData.exif[exifEntry].value.getString(0, 8) === "FUJIFILM") { | ||
this.extractMakernotes = require('./makernotes/fujifilm').extractMakernotes; | ||
} else { | ||
// Makernotes are available but the format is not recognized so | ||
// an error message is pushed instead, this ain't the best | ||
// solution but should do for now | ||
this.exifData.makernote.push({ error: 'Unable to extract Makernote information as it is in an unrecognized format.' }); | ||
break; | ||
} | ||
this.exifData.makernote = this.extractMakernotes(data, this.exifData.exif[exifEntry].valueOffset, tiffOffset); | ||
} | ||
} | ||
callback(false, this.exifData); | ||
return this.exifData; | ||
} | ||
}; | ||
ExifImage.prototype.extractExifEntry = function (data, entryOffset, tiffOffset, tags) { | ||
ExifImage.prototype.extractExifEntry = function (data, entryOffset, tiffOffset, isBigEndian, tags) { | ||
var entry = { | ||
tag : data.slice(entryOffset, entryOffset + 2), | ||
format : data.getShort(entryOffset + 2, this.isBigEndian), | ||
components : data.getLong(entryOffset + 4, this.isBigEndian), | ||
value : [] | ||
format : data.getShort(entryOffset + 2, isBigEndian), | ||
components : data.getLong(entryOffset + 4, isBigEndian), | ||
valueOffset: null, | ||
value : [] | ||
} | ||
entry.tagName = (tags && tags[entry.tag.getShort(0, this.isBigEndian)]) ? tags[entry.tag.getShort(0, this.isBigEndian)] : null; | ||
entry.tagName = (tags && tags[entry.tag.getShort(0, isBigEndian)]) ? tags[entry.tag.getShort(0, isBigEndian)] : null; | ||
@@ -267,57 +298,59 @@ switch (entry.format) { | ||
case 0x0001: // unsigned byte, 1 byte per component | ||
var offset = (entry.components <= 4) ? entryOffset + 8 : data.getLong(entryOffset + 8, this.isBigEndian) + tiffOffset; | ||
entry.valueOffset = (entry.components <= 4) ? entryOffset + 8 : data.getLong(entryOffset + 8, isBigEndian) + tiffOffset; | ||
for (var i = 0; i < entry.components; i++) | ||
entry.value.push(data.getByte(offset + i)); | ||
entry.value.push(data.getByte(entry.valueOffset + i)); | ||
break; | ||
case 0x0002: // ascii strings, 1 byte per component | ||
var offset = (entry.components <= 4) ? entryOffset + 8 : data.getLong(entryOffset + 8, this.isBigEndian) + tiffOffset; | ||
entry.value = data.getString(offset, entry.components - 1); | ||
entry.valueOffset = (entry.components <= 4) ? entryOffset + 8 : data.getLong(entryOffset + 8, isBigEndian) + tiffOffset; | ||
entry.value = data.getString(entry.valueOffset, entry.components); | ||
if (entry.value[entry.value.length - 1] === "\u0000") // Trim null terminated strings | ||
entry.value = entry.value.substring(0, entry.value.length - 1); | ||
break; | ||
case 0x0003: // unsigned short, 2 byte per component | ||
var offset = (entry.components <= 2) ? entryOffset + 8 : data.getLong(entryOffset + 8, this.isBigEndian) + tiffOffset; | ||
entry.valueOffset = (entry.components <= 2) ? entryOffset + 8 : data.getLong(entryOffset + 8, isBigEndian) + tiffOffset; | ||
for (var i = 0; i < entry.components; i++) | ||
entry.value.push(data.getShort(offset + i * 2, this.isBigEndian)); | ||
entry.value.push(data.getShort(entry.valueOffset + i * 2, isBigEndian)); | ||
break; | ||
case 0x0004: // unsigned long, 4 byte per component | ||
var offset = (entry.components == 1) ? entryOffset + 8 : data.getLong(entryOffset + 8, this.isBigEndian) + tiffOffset; | ||
entry.valueOffset = (entry.components == 1) ? entryOffset + 8 : data.getLong(entryOffset + 8, isBigEndian) + tiffOffset; | ||
for (var i = 0; i < entry.components; i++) | ||
entry.value.push(data.getLong(offset + i * 4, this.isBigEndian)); | ||
entry.value.push(data.getLong(entry.valueOffset + i * 4, isBigEndian)); | ||
break; | ||
case 0x0005: // unsigned rational, 8 byte per component (4 byte numerator and 4 byte denominator) | ||
var offset = data.getLong(entryOffset + 8, this.isBigEndian) + tiffOffset; | ||
entry.valueOffset = data.getLong(entryOffset + 8, isBigEndian) + tiffOffset; | ||
for (var i = 0; i < entry.components; i++) | ||
entry.value.push(data.getLong(offset, this.isBigEndian) / data.getLong(offset + 4, this.isBigEndian)); | ||
entry.value.push(data.getLong(entry.valueOffset + i * 8, isBigEndian) / data.getLong(entry.valueOffset + i * 8 + 4, isBigEndian)); | ||
break; | ||
case 0x0006: // signed byte, 1 byte per component | ||
var offset = (entry.components <= 4) ? entryOffset + 8 : data.getLong(entryOffset + 8, this.isBigEndian) + tiffOffset; | ||
entry.valueOffset = (entry.components <= 4) ? entryOffset + 8 : data.getLong(entryOffset + 8, isBigEndian) + tiffOffset; | ||
for (var i = 0; i < entry.components; i++) | ||
entry.value.push(data.getSignedByte(offset + i)); | ||
entry.value.push(data.getSignedByte(entry.valueOffset + i)); | ||
break; | ||
case 0x0007: // undefined, 1 byte per component | ||
var offset = (entry.components <= 4) ? entryOffset + 8 : data.getLong(entryOffset + 8, this.isBigEndian) + tiffOffset; | ||
entry.value.push(data.slice(offset, offset + entry.components)); | ||
entry.valueOffset = (entry.components <= 4) ? entryOffset + 8 : data.getLong(entryOffset + 8, isBigEndian) + tiffOffset; | ||
entry.value.push(data.slice(entry.valueOffset, entry.valueOffset + entry.components)); | ||
break; | ||
case 0x0008: // signed short, 2 byte per component | ||
var offset = (entry.components <= 2) ? entryOffset + 8 : data.getLong(entryOffset + 8, this.isBigEndian) + tiffOffset; | ||
entry.valueOffset = (entry.components <= 2) ? entryOffset + 8 : data.getLong(entryOffset + 8, isBigEndian) + tiffOffset; | ||
for (var i = 0; i < entry.components; i++) | ||
entry.value.push(data.getSignedShort(offset + i * 2, this.isBigEndian)); | ||
entry.value.push(data.getSignedShort(entry.valueOffset + i * 2, isBigEndian)); | ||
break; | ||
case 0x0009: // signed long, 4 byte per component | ||
var offset = (entry.components == 1) ? entryOffset + 8 : data.getLong(entryOffset + 8, this.isBigEndian) + tiffOffset; | ||
entry.valueOffset = (entry.components == 1) ? entryOffset + 8 : data.getLong(entryOffset + 8, isBigEndian) + tiffOffset; | ||
for (var i = 0; i < entry.components; i++) | ||
entry.value.push(data.getSignedLong(offset + i * 4, this.isBigEndian)); | ||
entry.value.push(data.getSignedLong(entry.valueOffset + i * 4, isBigEndian)); | ||
break; | ||
case 0x000A: // signed rational, 8 byte per component (4 byte numerator and 4 byte denominator) | ||
var offset = data.getLong(entryOffset + 8, this.isBigEndian) + tiffOffset; | ||
entry.valueOffset = data.getLong(entryOffset + 8, isBigEndian) + tiffOffset; | ||
for (var i = 0; i < entry.components; i++) | ||
entry.value.push(data.getSignedLong(offset, this.isBigEndian) / data.getSignedLong(offset + 4, this.isBigEndian)); | ||
entry.value.push(data.getSignedLong(entry.valueOffset + i * 8, isBigEndian) / data.getSignedLong(entry.valueOffset + i * 8 + 4, isBigEndian)); | ||
break; | ||
@@ -335,3 +368,3 @@ | ||
} | ||
}; | ||
@@ -402,3 +435,3 @@ /** | ||
0xA40C : 'SubjectDistanceRange', | ||
0xA420 : 'ImageUniqueID', | ||
0xA420 : 'ImageUniqueID' | ||
@@ -582,3 +615,3 @@ }, | ||
0xC65C : 'BestQualityScale', | ||
0xC660 : 'Alias Layer Metadata', | ||
0xC660 : 'Alias Layer Metadata' | ||
@@ -620,6 +653,6 @@ }, | ||
0x001D : 'GPSDateStamp', | ||
0x001E : 'GPSDifferential', | ||
0x001E : 'GPSDifferential' | ||
} | ||
} | ||
}; |
{ | ||
"name" : "exif", | ||
"version" : "0.1.0", | ||
"version" : "0.2.0", | ||
"description" : "A node.js library to extract Exif metadata from images.", | ||
"author" : "Daniel Leinich <leinich@gmx.net>", | ||
"keywords" : ["exif", "image", "jpeg", "jpg", "tiff", "makernotes", "gps"], | ||
"main" : "./lib/exif", | ||
@@ -7,0 +8,0 @@ "repository" : { |
node-exif | ||
========= | ||
With node-exif you can extract Exif metadata from images (JPEG). Exif is a | ||
With _node-exif_ you can extract Exif metadata from images (JPEG). Exif is a | ||
format used, for example, by digital cameras and scanners to save additional | ||
@@ -24,3 +24,3 @@ information about an image in the image file. This information can be the | ||
Easy. Just require node-exif and throw an image at it. If node-exif is able to | ||
Easy. Just require _node-exif_ and throw an image at it. If _node-exif_ is able to | ||
extract data from the image it does so and returns an object with all the | ||
@@ -27,0 +27,0 @@ information found, if an error occurs you will receive an error message. To |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Non-existent author
Supply chain riskThe package was published by an npm account that no longer exists.
Found 1 instance in 1 package
34100
12
796
0
1