119 lines
2.8 KiB
JavaScript
119 lines
2.8 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
// based on http://www.compix.com/fileformattif.htm
|
||
|
// TO-DO: support big-endian as well
|
||
|
|
||
|
var fs = require('fs');
|
||
|
var readUInt = require('../readUInt');
|
||
|
|
||
|
function isTIFF (buffer) {
|
||
|
var hex4 = buffer.toString('hex', 0, 4);
|
||
|
return ('49492a00' === hex4 || '4d4d002a' === hex4);
|
||
|
}
|
||
|
|
||
|
// Read IFD (image-file-directory) into a buffer
|
||
|
function readIFD (buffer, filepath, isBigEndian) {
|
||
|
|
||
|
var ifdOffset = readUInt(buffer, 32, 4, isBigEndian);
|
||
|
|
||
|
// read only till the end of the file
|
||
|
var bufferSize = 1024;
|
||
|
var fileSize = fs.statSync(filepath).size;
|
||
|
if (ifdOffset + bufferSize > fileSize) {
|
||
|
bufferSize = fileSize - ifdOffset - 10;
|
||
|
}
|
||
|
|
||
|
// populate the buffer
|
||
|
var endBuffer = new Buffer(bufferSize);
|
||
|
var descriptor = fs.openSync(filepath, 'r');
|
||
|
fs.readSync(descriptor, endBuffer, 0, bufferSize, ifdOffset);
|
||
|
|
||
|
// var ifdLength = readUInt(endBuffer, 16, 0, isBigEndian);
|
||
|
var ifdBuffer = endBuffer.slice(2); //, 2 + 12 * ifdLength);
|
||
|
return ifdBuffer;
|
||
|
}
|
||
|
|
||
|
// TIFF values seem to be messed up on Big-Endian, this helps
|
||
|
function readValue (buffer, isBigEndian) {
|
||
|
var low = readUInt(buffer, 16, 8, isBigEndian);
|
||
|
var high = readUInt(buffer, 16, 10, isBigEndian);
|
||
|
return (high << 16) + low;
|
||
|
}
|
||
|
|
||
|
// move to the next tag
|
||
|
function nextTag (buffer) {
|
||
|
if (buffer.length > 24) {
|
||
|
return buffer.slice(12);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Extract IFD tags from TIFF metadata
|
||
|
function extractTags (buffer, isBigEndian) {
|
||
|
var tags = {};
|
||
|
var code, type, length;
|
||
|
|
||
|
while (buffer && buffer.length) {
|
||
|
code = readUInt(buffer, 16, 0, isBigEndian);
|
||
|
type = readUInt(buffer, 16, 2, isBigEndian);
|
||
|
length = readUInt(buffer, 32, 4, isBigEndian);
|
||
|
|
||
|
// 0 means end of IFD
|
||
|
if (code === 0) {
|
||
|
break;
|
||
|
} else {
|
||
|
// 256 is width, 257 is height
|
||
|
// if (code === 256 || code === 257) {
|
||
|
if (length === 1 && (type === 3 || type === 4)) {
|
||
|
tags[code] = readValue(buffer, isBigEndian);
|
||
|
}
|
||
|
|
||
|
// move to the next tag
|
||
|
buffer = nextTag(buffer);
|
||
|
}
|
||
|
}
|
||
|
return tags;
|
||
|
}
|
||
|
|
||
|
// Test if the TIFF is Big Endian or Little Endian
|
||
|
function determineEndianness (buffer) {
|
||
|
var signature = buffer.toString('ascii', 0, 2);
|
||
|
if ('II' === signature) {
|
||
|
return 'LE';
|
||
|
} else if ('MM' === signature) {
|
||
|
return 'BE';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function calculate (buffer, filepath) {
|
||
|
|
||
|
if (!filepath) {
|
||
|
throw new TypeError('Tiff doesn\'t support buffer');
|
||
|
}
|
||
|
|
||
|
// Determine BE/LE
|
||
|
var isBigEndian = determineEndianness(buffer) === 'BE';
|
||
|
|
||
|
// read the IFD
|
||
|
var ifdBuffer = readIFD(buffer, filepath, isBigEndian);
|
||
|
|
||
|
// extract the tags from the IFD
|
||
|
var tags = extractTags(ifdBuffer, isBigEndian);
|
||
|
|
||
|
var width = tags[256];
|
||
|
var height = tags[257];
|
||
|
|
||
|
if (!width || !height) {
|
||
|
throw new TypeError('Invalid Tiff, missing tags');
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
'width': width,
|
||
|
'height': height
|
||
|
};
|
||
|
}
|
||
|
|
||
|
module.exports = {
|
||
|
'detect': isTIFF,
|
||
|
'calculate': calculate
|
||
|
};
|