1 | 'use strict';
|
---|
2 |
|
---|
3 | // based on http://www.compix.com/fileformattif.htm
|
---|
4 | // TO-DO: support big-endian as well
|
---|
5 |
|
---|
6 | var fs = require('fs');
|
---|
7 | var readUInt = require('../readUInt');
|
---|
8 |
|
---|
9 | function isTIFF (buffer) {
|
---|
10 | var hex4 = buffer.toString('hex', 0, 4);
|
---|
11 | return ('49492a00' === hex4 || '4d4d002a' === hex4);
|
---|
12 | }
|
---|
13 |
|
---|
14 | // Read IFD (image-file-directory) into a buffer
|
---|
15 | function readIFD (buffer, filepath, isBigEndian) {
|
---|
16 |
|
---|
17 | var ifdOffset = readUInt(buffer, 32, 4, isBigEndian);
|
---|
18 |
|
---|
19 | // read only till the end of the file
|
---|
20 | var bufferSize = 1024;
|
---|
21 | var fileSize = fs.statSync(filepath).size;
|
---|
22 | if (ifdOffset + bufferSize > fileSize) {
|
---|
23 | bufferSize = fileSize - ifdOffset - 10;
|
---|
24 | }
|
---|
25 |
|
---|
26 | // populate the buffer
|
---|
27 | var endBuffer = new Buffer(bufferSize);
|
---|
28 | var descriptor = fs.openSync(filepath, 'r');
|
---|
29 | fs.readSync(descriptor, endBuffer, 0, bufferSize, ifdOffset);
|
---|
30 |
|
---|
31 | // var ifdLength = readUInt(endBuffer, 16, 0, isBigEndian);
|
---|
32 | var ifdBuffer = endBuffer.slice(2); //, 2 + 12 * ifdLength);
|
---|
33 | return ifdBuffer;
|
---|
34 | }
|
---|
35 |
|
---|
36 | // TIFF values seem to be messed up on Big-Endian, this helps
|
---|
37 | function readValue (buffer, isBigEndian) {
|
---|
38 | var low = readUInt(buffer, 16, 8, isBigEndian);
|
---|
39 | var high = readUInt(buffer, 16, 10, isBigEndian);
|
---|
40 | return (high << 16) + low;
|
---|
41 | }
|
---|
42 |
|
---|
43 | // move to the next tag
|
---|
44 | function nextTag (buffer) {
|
---|
45 | if (buffer.length > 24) {
|
---|
46 | return buffer.slice(12);
|
---|
47 | }
|
---|
48 | }
|
---|
49 |
|
---|
50 | // Extract IFD tags from TIFF metadata
|
---|
51 | function extractTags (buffer, isBigEndian) {
|
---|
52 | var tags = {};
|
---|
53 | var code, type, length;
|
---|
54 |
|
---|
55 | while (buffer && buffer.length) {
|
---|
56 | code = readUInt(buffer, 16, 0, isBigEndian);
|
---|
57 | type = readUInt(buffer, 16, 2, isBigEndian);
|
---|
58 | length = readUInt(buffer, 32, 4, isBigEndian);
|
---|
59 |
|
---|
60 | // 0 means end of IFD
|
---|
61 | if (code === 0) {
|
---|
62 | break;
|
---|
63 | } else {
|
---|
64 | // 256 is width, 257 is height
|
---|
65 | // if (code === 256 || code === 257) {
|
---|
66 | if (length === 1 && (type === 3 || type === 4)) {
|
---|
67 | tags[code] = readValue(buffer, isBigEndian);
|
---|
68 | }
|
---|
69 |
|
---|
70 | // move to the next tag
|
---|
71 | buffer = nextTag(buffer);
|
---|
72 | }
|
---|
73 | }
|
---|
74 | return tags;
|
---|
75 | }
|
---|
76 |
|
---|
77 | // Test if the TIFF is Big Endian or Little Endian
|
---|
78 | function determineEndianness (buffer) {
|
---|
79 | var signature = buffer.toString('ascii', 0, 2);
|
---|
80 | if ('II' === signature) {
|
---|
81 | return 'LE';
|
---|
82 | } else if ('MM' === signature) {
|
---|
83 | return 'BE';
|
---|
84 | }
|
---|
85 | }
|
---|
86 |
|
---|
87 | function calculate (buffer, filepath) {
|
---|
88 |
|
---|
89 | if (!filepath) {
|
---|
90 | throw new TypeError('Tiff doesn\'t support buffer');
|
---|
91 | }
|
---|
92 |
|
---|
93 | // Determine BE/LE
|
---|
94 | var isBigEndian = determineEndianness(buffer) === 'BE';
|
---|
95 |
|
---|
96 | // read the IFD
|
---|
97 | var ifdBuffer = readIFD(buffer, filepath, isBigEndian);
|
---|
98 |
|
---|
99 | // extract the tags from the IFD
|
---|
100 | var tags = extractTags(ifdBuffer, isBigEndian);
|
---|
101 |
|
---|
102 | var width = tags[256];
|
---|
103 | var height = tags[257];
|
---|
104 |
|
---|
105 | if (!width || !height) {
|
---|
106 | throw new TypeError('Invalid Tiff, missing tags');
|
---|
107 | }
|
---|
108 |
|
---|
109 | return {
|
---|
110 | 'width': width,
|
---|
111 | 'height': height
|
---|
112 | };
|
---|
113 | }
|
---|
114 |
|
---|
115 | module.exports = {
|
---|
116 | 'detect': isTIFF,
|
---|
117 | 'calculate': calculate
|
---|
118 | };
|
---|