UNPKG

decode-tiff

Version:
351 lines (323 loc) 10.4 kB
"use strict"; /** * * Required Fields for Bilevel Images * * ImageWidth 256 100 SHORT or LONG * ImageLength 257 101 SHORT or LONG * Compression 259 103 SHORT 1, 2 or 32773 * PhotometricInterpretation 262 106 SHORT 0 or 1 * StripOffsets 273 111 SHORT or LONG * RowsPerStrip 278 116 SHORT or LONG * StripByteCounts 279 117 LONG or SHORT * XResolution 282 11A RATIONAL * YResolution 283 11B RATIONAL * ResolutionUnit 296 128 SHORT 1, 2 or 3 * **/ var TAG_NAME_MAP = { 0x0100: "imageWidth", 0x0101: "imageLength", 0x0102: "bitsPerSample", 0x0103: "compression", 0x0106: "photometricInterpretation", 0x0111: "stripOffsets", 0x0116: "rowsPerStrip", 0x0117: "stripByteCounts", 0x0128: "resolutionUnit", 0x0140: "colorMap" }; function loadPages(buf) { var idx = 0; var isMSB = void 0; var ifdEntries = {}; var stripData = void 0; function read(offset, length) { var begin = offset, end = offset + length; if (isMSB) { return buf.subarray(begin, end); } else { var s = buf.subarray(begin, end); var x = new Uint8Array(end - begin); for (var i = 0; i < s.byteLength; i++) { x[s.byteLength - i - 1] = s[i]; } return x; } } function readAsUint16(offset) { var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; var force = arguments[2]; if (isMSB) { var dd = new DataView(buf.buffer); if (length > 1 || force) { var y = new Uint16Array(length); for (var i = 0; i < length; i++) { y[i] = dd.getUint16(offset + (i << 1)); } return y; } else { return dd.getUint16(offset); } } else { var d = new DataView(read(offset, length << 1).buffer); if (length > 1 || force) { var x = new Uint16Array(length); for (var _i = 0; _i < length; _i++) { x[_i] = d.getUint16(_i << 1); } return x; } else { return d.getUint16(0); } } } function readAsUint32(offset) { var length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; var force = arguments[2]; if (isMSB) { var dd = new DataView(buf.buffer); if (length > 1 || force) { var y = new Uint32Array(length); for (var i = 0; i < length; i++) { y[i] = dd.getUint32(offset + (i << 2)); } return y; } else { return dd.getUint32(offset); } } else { var d = new DataView(read(offset, length << 2).buffer); if (length > 1 || force) { var x = new Uint32Array(length); for (var _i2 = 0; _i2 < length; _i2++) { x[_i2] = d.getUint32(_i2 << 2); } return x; } else { return d.getUint32(0); } } } /** * * The field types and their sizes are: * * 1 = BYTE 8-bit unsigned integer. * 2 = ASCII 8-bit byte that contains a 7-bit ASCII code; the last byte must be NUL (binary zero). * 3 = SHORT 16-bit (2-byte) unsigned integer. * 4 = LONG 32-bit (4-byte) unsigned integer. * 5 = RATIONAL Two LONGs: the first represents the numerator of a fraction; the second, the denominator. * * In TIFF 6.0, some new field types have been defined: * * 6 = SBYTE An 8-bit signed (twos-complement) integer. * 7 = UNDEFINED An 8-bit byte that may contain anything, depending on the definition of the field. * 8 = SSHORT A 16-bit (2-byte) signed (twos-complement) integer. * 9 = SLONG A 32-bit (4-byte) signed (twos-complement) integer. * 10 = SRATIONAL Two SLONG’s: the first represents the numerator of a fraction, the second the denominator. * 11 = FLOAT Single precision (4-byte) IEEE format. * 12 = DOUBLE Double precision (8-byte) IEEE format * **/ function byteLength(fieldType, numOfValues) { switch (fieldType) { case 1: return numOfValues; case 3: return numOfValues << 1; case 4: return numOfValues << 2; case 5: return numOfValues << 3; default: return numOfValues << 2; } } function parseIFDFieldValueToArray(fieldType, numOfValues, valueOffset) { var bl = byteLength(fieldType, numOfValues); var l = void 0; if (bl > 4) valueOffset = readAsUint32(valueOffset); if (bl < 4) { l = 4 / bl; } else { l = numOfValues; } var x = void 0; switch (fieldType) { case 1: break; case 3: x = readAsUint16(valueOffset, l, true); break; case 4: x = readAsUint32(valueOffset, l, true); break; } if (!x) return; if (bl < 4) { return isMSB ? x.slice(0, l - numOfValues) : x.slice(l - numOfValues); } else { return x; } } function parseIFDEntry(tagId, fieldType, numOfValues, valueOffset) { var k = TAG_NAME_MAP[tagId]; if (k) { ifdEntries[k] = parseIFDFieldValueToArray(fieldType, numOfValues, valueOffset); } else { // TODO // console.log("unknown IFD entry: ", tagId, fieldType, numOfValues, valueOffset); } } function readStrips(ifdEntries) { var ret = new Uint8Array(ifdEntries.stripByteCounts.reduce(function (s, b) { return s + b; }, 0)); var copiedBl = 0; for (var s = 0; s < ifdEntries.stripOffsets.length; s++) { var x = buf.subarray(ifdEntries.stripOffsets[s], ifdEntries.stripOffsets[s] + ifdEntries.stripByteCounts[s]); ret.set(x, copiedBl); copiedBl += x.byteLength; } return ret; } // Image File Header // Byte order if (buf[0] === 0x4d && buf[1] === 0x4d) { isMSB = true; } else if (buf[0] === 0x49 && buf[1] === 0x49) { isMSB = false; } else { throw new Error("Invalid byte order " + buf[0] + buf[1]); } if (read(2, 2)[1] !== 0x2a) { throw new Error("not tiff"); } // console.log(readAsUint32(4), read(4, 4)); var pages = []; for (var ifdOffset = readAsUint32(4); ifdOffset !== 0; ifdOffset = readAsUint32(idx)) { // Number of Directory Entries idx = ifdOffset; var numOfIFD = readAsUint16(idx); ifdEntries = {}; // IFD Entries idx += 2; for (var i = 0; i < numOfIFD; i++) { // TAG var tagId = readAsUint16(idx); // Field type idx += 2; var fieldType = readAsUint16(idx); // The number of values idx += 2; var numOfValues = readAsUint32(idx); // The value offset idx += 4; var valueOffset = idx; parseIFDEntry(tagId, fieldType, numOfValues, valueOffset); idx += 4; } stripData = readStrips(ifdEntries); pages.push({ stripData: stripData, ifdEntries: ifdEntries }); } return pages; } function decompressData(ifdEntries, stripData) { var compression = ifdEntries.compression; if (!compression || compression[0] === 1) { // no-compress return stripData; } else if (compression[0] === 2) { // CCITT Group 3 throw new Error("CCITT group3 decompressionion is not implemented."); } else if (compression[0] === 5) { // LZW throw new Error("LZW decompressionion is not implemented."); } else if (compression[0] === 6) { // JPEG throw new Error("JPEG decompressionion is not implemented."); } else if (compression[0] === 7) { // JPEG2 throw new Error("JPEG2 decompressionion is not implemented."); } else if (compression[0] === 8) { // Zip(Adobe Deflate) throw new Error("Zip (Adove Deflate) decompressionion is not implemented."); } else if (compression[0] === 32773) { // Packbits throw new Error("Packbits decompression is not implemented."); } else { throw new Error("Unknown compression type: " + compression[0]); } } function normalizeStripData(ifdEntries, stripData) { var colorMap = ifdEntries.colorMap, bitsPerSample = ifdEntries.bitsPerSample, photometricInterpretation = ifdEntries.photometricInterpretation; var x = void 0; stripData = decompressData(ifdEntries, stripData); if (!bitsPerSample) { throw new Error("Bilevel image decode is not implemented."); } if (colorMap) { throw new Error("Palette-color image decode is not implemented."); } if (photometricInterpretation[0] = 2 && bitsPerSample.length === 4) { // 32bit RBGA image return stripData; } else if (photometricInterpretation[0] = 2 && bitsPerSample.length === 3) { // 24bit RBG image x = new Uint8Array(stripData.length / 3 * 4); for (var i = 0; i < stripData.length / 3; i++) { x[i * 4] = stripData[i * 3]; x[i * 4 + 1] = stripData[i * 3 + 1]; x[i * 4 + 2] = stripData[i * 3 + 2]; x[i * 4 + 3] = 0xFf; } return x; } else if (photometricInterpretation[0] < 2 && bitsPerSample.length === 1 && bitsPerSample[0] === 4) { // 4bit grayscale image x = new Uint8Array(stripData.length * 4); for (var _i3 = 0; _i3 < stripData.length; _i3++) { x[_i3 * 4] = stripData[_i3] << 4; x[_i3 * 4 + 1] = stripData[_i3 + 1] << 4; x[_i3 * 4 + 2] = stripData[_i3 + 2] << 4; x[_i3 * 4 + 3] = 0xFF; } return x; } else if (photometricInterpretation[0] < 2 && bitsPerSample.length === 1 && bitsPerSample[0] === 8) { // 8bit grayscale image x = new Uint8Array(stripData.length * 4); for (var _i4 = 0; _i4 < stripData.length; _i4++) { x[_i4 * 4] = stripData[_i4]; x[_i4 * 4 + 1] = stripData[_i4 + 1]; x[_i4 * 4 + 2] = stripData[_i4 + 2]; x[_i4 * 4 + 3] = 0xFF; } return x; } else { throw new Error("Can't detect image type. PhotometricInterpretation: " + photometricInterpretation[0] + ", BitsPerSample: " + bitsPerSample); } } function decode(buf) { var opt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { singlePage: true }; var rawPages = loadPages(new Uint8Array(buf)); var pages = rawPages.map(function (rawPage) { var width = rawPage.ifdEntries.imageWidth[0]; var height = rawPage.ifdEntries.imageLength[0]; var data = normalizeStripData(rawPage.ifdEntries, rawPage.stripData); return { width: width, height: height, data: data, ifdEntries: rawPage.ifdEntries }; }); if (opt.singlePage) { if (!pages || !pages.length) { throw new Error("No pages"); } return pages[0]; } else { return pages; } } module.exports = { decode: decode };