'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 };