UNPKG

@chalkbooks/react-doc-viewer

Version:
604 lines (603 loc) 23.7 kB
var _this = this; /* eslint-disable */ // @ts-nocheck var tiffDataView = undefined; var littleEndian = undefined; var fileDirectories = []; var isLittleEndian = function () { // Get byte order mark. var BOM = getBytes(2, 0); // Find out the endianness. if (BOM === 0x4949) { littleEndian = true; } else if (BOM === 0x4d4d) { littleEndian = false; } else { throw TypeError("Invalid byte order value."); } return littleEndian; }; var hasTowel = function () { // Check for towel. if (getBytes(2, 2) !== 42) { throw RangeError("You forgot your towel!"); return false; } return true; }; var getFieldTagName = function (fieldTag) { // See: http://www.digitizationguidelines.gov/guidelines/TIFF_Metadata_Final.pdf // See: http://www.digitalpreservation.gov/formats/content/tiff_tags.shtml var fieldTagNames = { // TIFF Baseline 0x013b: "Artist", 0x0102: "BitsPerSample", 0x0109: "CellLength", 0x0108: "CellWidth", 0x0140: "ColorMap", 0x0103: "Compression", 0x8298: "Copyright", 0x0132: "DateTime", 0x0152: "ExtraSamples", 0x010a: "FillOrder", 0x0121: "FreeByteCounts", 0x0120: "FreeOffsets", 0x0123: "GrayResponseCurve", 0x0122: "GrayResponseUnit", 0x013c: "HostComputer", 0x010e: "ImageDescription", 0x0101: "ImageLength", 0x0100: "ImageWidth", 0x010f: "Make", 0x0119: "MaxSampleValue", 0x0118: "MinSampleValue", 0x0110: "Model", 0x00fe: "NewSubfileType", 0x0112: "Orientation", 0x0106: "PhotometricInterpretation", 0x011c: "PlanarConfiguration", 0x0128: "ResolutionUnit", 0x0116: "RowsPerStrip", 0x0115: "SamplesPerPixel", 0x0131: "Software", 0x0117: "StripByteCounts", 0x0111: "StripOffsets", 0x00ff: "SubfileType", 0x0107: "Threshholding", 0x011a: "XResolution", 0x011b: "YResolution", // TIFF Extended 0x0146: "BadFaxLines", 0x0147: "CleanFaxData", 0x0157: "ClipPath", 0x0148: "ConsecutiveBadFaxLines", 0x01b1: "Decode", 0x01b2: "DefaultImageColor", 0x010d: "DocumentName", 0x0150: "DotRange", 0x0141: "HalftoneHints", 0x015a: "Indexed", 0x015b: "JPEGTables", 0x011d: "PageName", 0x0129: "PageNumber", 0x013d: "Predictor", 0x013f: "PrimaryChromaticities", 0x0214: "ReferenceBlackWhite", 0x0153: "SampleFormat", 0x022f: "StripRowCounts", 0x014a: "SubIFDs", 0x0124: "T4Options", 0x0125: "T6Options", 0x0145: "TileByteCounts", 0x0143: "TileLength", 0x0144: "TileOffsets", 0x0142: "TileWidth", 0x012d: "TransferFunction", 0x013e: "WhitePoint", 0x0158: "XClipPathUnits", 0x011e: "XPosition", 0x0211: "YCbCrCoefficients", 0x0213: "YCbCrPositioning", 0x0212: "YCbCrSubSampling", 0x0159: "YClipPathUnits", 0x011f: "YPosition", // EXIF 0x9202: "ApertureValue", 0xa001: "ColorSpace", 0x9004: "DateTimeDigitized", 0x9003: "DateTimeOriginal", 0x8769: "Exif IFD", 0x9000: "ExifVersion", 0x829a: "ExposureTime", 0xa300: "FileSource", 0x9209: "Flash", 0xa000: "FlashpixVersion", 0x829d: "FNumber", 0xa420: "ImageUniqueID", 0x9208: "LightSource", 0x927c: "MakerNote", 0x9201: "ShutterSpeedValue", 0x9286: "UserComment", // IPTC 0x83bb: "IPTC", // ICC 0x8773: "ICC Profile", // XMP 0x02bc: "XMP", // GDAL 0xa480: "GDAL_METADATA", 0xa481: "GDAL_NODATA", // Photoshop 0x8649: "Photoshop", }; var fieldTagName; if (fieldTag in fieldTagNames) { fieldTagName = fieldTagNames[fieldTag]; } else { fieldTagName = "Tag" + fieldTag; } return fieldTagName; }; var getFieldTypeName = function (fieldType) { var fieldTypeNames = { 0x0001: "BYTE", 0x0002: "ASCII", 0x0003: "SHORT", 0x0004: "LONG", 0x0005: "RATIONAL", 0x0006: "SBYTE", 0x0007: "UNDEFINED", 0x0008: "SSHORT", 0x0009: "SLONG", 0x000a: "SRATIONAL", 0x000b: "FLOAT", 0x000c: "DOUBLE", }; var fieldTypeName; if (fieldType in fieldTypeNames) { fieldTypeName = fieldTypeNames[fieldType]; } return fieldTypeName; }; var getFieldTypeLength = function (fieldTypeName) { var fieldTypeLength; if (["BYTE", "ASCII", "SBYTE", "UNDEFINED"].indexOf(fieldTypeName) !== -1) { fieldTypeLength = 1; } else if (["SHORT", "SSHORT"].indexOf(fieldTypeName) !== -1) { fieldTypeLength = 2; } else if (["LONG", "SLONG", "FLOAT"].indexOf(fieldTypeName) !== -1) { fieldTypeLength = 4; } else if (["RATIONAL", "SRATIONAL", "DOUBLE"].indexOf(fieldTypeName) !== -1) { fieldTypeLength = 8; } return fieldTypeLength; }; var getBits = function (numBits, byteOffset, bitOffset) { bitOffset = bitOffset || 0; var extraBytes = Math.floor(bitOffset / 8); var newByteOffset = byteOffset + extraBytes; var totalBits = bitOffset + numBits; var shiftRight = 32 - numBits; if (totalBits <= 0) { throw RangeError("No bits requested"); } else if (totalBits <= 8) { var shiftLeft = 24 + bitOffset; var rawBits = tiffDataView.getUint8(newByteOffset, littleEndian); } else if (totalBits <= 16) { var shiftLeft = 16 + bitOffset; var rawBits = tiffDataView.getUint16(newByteOffset, littleEndian); } else if (totalBits <= 32) { var shiftLeft = bitOffset; var rawBits = tiffDataView.getUint32(newByteOffset, littleEndian); } else { throw RangeError("Too many bits requested"); } var chunkInfo = { bits: (rawBits << shiftLeft) >>> shiftRight, byteOffset: newByteOffset + Math.floor(totalBits / 8), bitOffset: totalBits % 8, }; return chunkInfo; }; var getBytes = function (numBytes, offset) { if (numBytes <= 0) { throw RangeError("No bytes requested"); } else if (numBytes <= 1) { return tiffDataView.getUint8(offset, littleEndian); } else if (numBytes <= 2) { return tiffDataView.getUint16(offset, littleEndian); } else if (numBytes <= 3) { return tiffDataView.getUint32(offset, littleEndian) >>> 8; } else if (numBytes <= 4) { return tiffDataView.getUint32(offset, littleEndian); } else { throw RangeError("Too many bytes requested"); } }; var getFieldValues = function (fieldTagName, fieldTypeName, typeCount, valueOffset) { var fieldValues = []; var fieldTypeLength = getFieldTypeLength(fieldTypeName); var fieldValueSize = fieldTypeLength * typeCount; if (fieldValueSize <= 4) { // The value is stored at the big end of the valueOffset. if (littleEndian === false) { var value = valueOffset >>> ((4 - fieldTypeLength) * 8); } else { var value = valueOffset; } fieldValues.push(value); } else { for (var i = 0; i < typeCount; i++) { var indexOffset = fieldTypeLength * i; if (fieldTypeLength >= 8) { if (["RATIONAL", "SRATIONAL"].indexOf(fieldTypeName) !== -1) { // Numerator fieldValues.push(getBytes(4, valueOffset + indexOffset)); // Denominator fieldValues.push(getBytes(4, valueOffset + indexOffset + 4)); // } else if (['DOUBLE'].indexOf(fieldTypeName) !== -1) { // fieldValues.push(getBytes(4, valueOffset + indexOffset) + getBytes(4, valueOffset + indexOffset + 4)); } else { throw TypeError("Can't handle this field type or size"); } } else { fieldValues.push(getBytes(fieldTypeLength, valueOffset + indexOffset)); } } } if (fieldTypeName === "ASCII") { fieldValues.forEach(function (e, i, a) { a[i] = String.fromCharCode(e); }); } return fieldValues; }; var clampColorSample = function (colorSample, bitsPerSample) { var multiplier = Math.pow(2, 8 - bitsPerSample); return Math.floor(colorSample * multiplier + (multiplier - 1)); }; var makeRGBAFillValue = function (r, g, b, a) { if (typeof a === "undefined") { a = 1.0; } return "rgba(" + r + ", " + g + ", " + b + ", " + a + ")"; }; var parseFileDirectory = function (byteOffset) { var numDirEntries = getBytes(2, byteOffset); var tiffFields = []; for (var i = byteOffset + 2, entryCount = 0; entryCount < numDirEntries; i += 12, entryCount++) { var fieldTag = getBytes(2, i); var fieldType = getBytes(2, i + 2); var typeCount = getBytes(4, i + 4); var valueOffset = getBytes(4, i + 8); var fieldTagName = getFieldTagName(fieldTag); var fieldTypeName = getFieldTypeName(fieldType); var fieldValues = getFieldValues(fieldTagName, fieldTypeName, typeCount, valueOffset); tiffFields[fieldTagName] = { type: fieldTypeName, values: fieldValues }; } fileDirectories.push(tiffFields); var nextIFDByteOffset = getBytes(4, i); if (nextIFDByteOffset === 0x00000000) { return fileDirectories; } else { return parseFileDirectory(nextIFDByteOffset); } }; export var parseTIFF = function (tiffArrayBuffer, _canvas) { var canvas = _canvas || document.createElement("canvas"); if (!tiffArrayBuffer) return; tiffDataView = new DataView(tiffArrayBuffer); // canvas = _canvas; littleEndian = isLittleEndian(tiffDataView); if (!hasTowel(tiffDataView, littleEndian)) return; var firstIFDByteOffset = getBytes(4, 4); fileDirectories = parseFileDirectory(firstIFDByteOffset); var fileDirectory = fileDirectories[0]; var imageWidth = fileDirectory.ImageWidth.values[0]; var imageLength = fileDirectory.ImageLength.values[0]; canvas.width = imageWidth; canvas.height = imageLength; var strips = []; var compression = fileDirectory.Compression ? fileDirectory.Compression.values[0] : 1; var samplesPerPixel = fileDirectory.SamplesPerPixel.values[0]; var sampleProperties = []; var bitsPerPixel = 0; var hasBytesPerPixel = false; fileDirectory.BitsPerSample.values.forEach(function (bitsPerSample, i, bitsPerSampleValues) { sampleProperties[i] = { bitsPerSample: bitsPerSample, hasBytesPerSample: false, bytesPerSample: undefined, }; if (bitsPerSample % 8 === 0) { sampleProperties[i].hasBytesPerSample = true; sampleProperties[i].bytesPerSample = bitsPerSample / 8; } bitsPerPixel += bitsPerSample; }, _this); if (bitsPerPixel % 8 === 0) { hasBytesPerPixel = true; var bytesPerPixel = bitsPerPixel / 8; } var stripOffsetValues = fileDirectory.StripOffsets.values; var numStripOffsetValues = stripOffsetValues.length; // StripByteCounts is supposed to be required, but see if we can recover anyway. if (fileDirectory.StripByteCounts) { var stripByteCountValues = fileDirectory.StripByteCounts.values; } else { // Infer StripByteCounts, if possible. if (numStripOffsetValues === 1) { var stripByteCountValues = [ Math.ceil((imageWidth * imageLength * bitsPerPixel) / 8), ]; } else { throw Error("Cannot recover from missing StripByteCounts"); } } // Loop through strips and decompress as necessary. for (var i = 0; i < numStripOffsetValues; i++) { var stripOffset = stripOffsetValues[i]; strips[i] = []; var stripByteCount = stripByteCountValues[i]; // Loop through pixels. for (var byteOffset = 0, bitOffset = 0, jIncrement = 1, getHeader = true, pixel = [], numBytes = 0, sample = 0, currentSample = 0; byteOffset < stripByteCount; byteOffset += jIncrement) { // Decompress strip. switch (compression) { // Uncompressed case 1: // Loop through samples (sub-pixels). for (var m = 0, pixel = []; m < samplesPerPixel; m++) { if (sampleProperties[m].hasBytesPerSample) { // XXX: This is wrong! var sampleOffset = sampleProperties[m].bytesPerSample * m; pixel.push(getBytes(sampleProperties[m].bytesPerSample, stripOffset + byteOffset + sampleOffset)); } else { var sampleInfo = getBits(sampleProperties[m].bitsPerSample, stripOffset + byteOffset, bitOffset); pixel.push(sampleInfo.bits); byteOffset = sampleInfo.byteOffset - stripOffset; bitOffset = sampleInfo.bitOffset; throw RangeError("Cannot handle sub-byte bits per sample"); } } strips[i].push(pixel); if (hasBytesPerPixel) { jIncrement = bytesPerPixel; } else { jIncrement = 0; throw RangeError("Cannot handle sub-byte bits per pixel"); } break; // CITT Group 3 1-Dimensional Modified Huffman run-length encoding case 2: // XXX: Use PDF.js code? break; // Group 3 Fax case 3: // XXX: Use PDF.js code? break; // Group 4 Fax case 4: // XXX: Use PDF.js code? break; // LZW case 5: // XXX: Use PDF.js code? break; // Old-style JPEG (TIFF 6.0) case 6: // XXX: Use PDF.js code? break; // New-style JPEG (TIFF Specification Supplement 2) case 7: // XXX: Use PDF.js code? break; // PackBits case 32773: // Are we ready for a new block? if (getHeader) { getHeader = false; var blockLength = 1; var iterations = 1; // The header byte is signed. var header = tiffDataView.getInt8(stripOffset + byteOffset, littleEndian); if (header >= 0 && header <= 127) { // Normal pixels. blockLength = header + 1; } else if (header >= -127 && header <= -1) { // Collapsed pixels. iterations = -header + 1; } /*if (header === -128)*/ else { // Placeholder byte? getHeader = true; } } else { var currentByte = getBytes(1, stripOffset + byteOffset); // Duplicate bytes, if necessary. for (var m = 0; m < iterations; m++) { if (sampleProperties[sample].hasBytesPerSample) { // We're reading one byte at a time, so we need to handle multi-byte samples. currentSample = (currentSample << (8 * numBytes)) | currentByte; numBytes++; // Is our sample complete? if (numBytes === sampleProperties[sample].bytesPerSample) { pixel.push(currentSample); currentSample = numBytes = 0; sample++; } } else { throw RangeError("Cannot handle sub-byte bits per sample"); } // Is our pixel complete? if (sample === samplesPerPixel) { strips[i].push(pixel); pixel = []; sample = 0; } } blockLength--; // Is our block complete? if (blockLength === 0) { getHeader = true; } } jIncrement = 1; break; // Unknown compression algorithm default: // Do not attempt to parse the image data. break; } } } if (canvas.getContext) { var ctx = canvas.getContext("2d"); // Set a default fill style. ctx.fillStyle = makeRGBAFillValue(255, 255, 255, 0); // If RowsPerStrip is missing, the whole image is in one strip. if (fileDirectory.RowsPerStrip) { var rowsPerStrip = fileDirectory.RowsPerStrip.values[0]; } else { var rowsPerStrip = imageLength; } var numStrips = strips.length; var imageLengthModRowsPerStrip = imageLength % rowsPerStrip; var rowsInLastStrip = imageLengthModRowsPerStrip === 0 ? rowsPerStrip : imageLengthModRowsPerStrip; var numRowsInStrip = rowsPerStrip; var numRowsInPreviousStrip = 0; var photometricInterpretation = fileDirectory.PhotometricInterpretation.values[0]; var extraSamplesValues = []; var numExtraSamples = 0; if (fileDirectory.ExtraSamples) { extraSamplesValues = fileDirectory.ExtraSamples.values; numExtraSamples = extraSamplesValues.length; } if (fileDirectory.ColorMap) { var colorMapValues = fileDirectory.ColorMap.values; var colorMapSampleSize = Math.pow(2, sampleProperties[0].bitsPerSample); } // Loop through the strips in the image. for (var i = 0; i < numStrips; i++) { // The last strip may be short. if (i + 1 === numStrips) { numRowsInStrip = rowsInLastStrip; } var numPixels = strips[i].length; var yPadding = numRowsInPreviousStrip * i; // Loop through the rows in the strip. for (var y = 0, j = 0; y < numRowsInStrip, j < numPixels; y++) { // Loop through the pixels in the row. for (var x = 0; x < imageWidth; x++, j++) { var pixelSamples = strips[i][j]; var red = 0; var green = 0; var blue = 0; var opacity = 1.0; if (numExtraSamples > 0) { for (var k = 0; k < numExtraSamples; k++) { if (extraSamplesValues[k] === 1 || extraSamplesValues[k] === 2) { // Clamp opacity to the range [0,1]. opacity = pixelSamples[3 + k] / 256; break; } } } switch (photometricInterpretation) { // Bilevel or Grayscale // WhiteIsZero case 0: if (sampleProperties[0].hasBytesPerSample) { var invertValue = Math.pow(0x10, sampleProperties[0].bytesPerSample * 2); } // Invert samples. pixelSamples.forEach(function (sample, index, samples) { samples[index] = invertValue - sample; }); // Bilevel or Grayscale // BlackIsZero case 1: red = green = blue = clampColorSample(pixelSamples[0], sampleProperties[0].bitsPerSample); break; // RGB Full Color case 2: red = clampColorSample(pixelSamples[0], sampleProperties[0].bitsPerSample); green = clampColorSample(pixelSamples[1], sampleProperties[1].bitsPerSample); blue = clampColorSample(pixelSamples[2], sampleProperties[2].bitsPerSample); break; // RGB Color Palette case 3: if (colorMapValues === undefined) { throw Error("Palette image missing color map"); } var colorMapIndex = pixelSamples[0]; red = clampColorSample(colorMapValues[colorMapIndex], 16); green = clampColorSample(colorMapValues[colorMapSampleSize + colorMapIndex], 16); blue = clampColorSample(colorMapValues[2 * colorMapSampleSize + colorMapIndex], 16); break; // Transparency mask case 4: throw RangeError("Not Yet Implemented: Transparency mask"); break; // CMYK case 5: throw RangeError("Not Yet Implemented: CMYK"); break; // YCbCr case 6: throw RangeError("Not Yet Implemented: YCbCr"); break; // CIELab case 8: throw RangeError("Not Yet Implemented: CIELab"); break; // Unknown Photometric Interpretation default: throw RangeError("Unknown Photometric Interpretation:", photometricInterpretation); break; } ctx.fillStyle = makeRGBAFillValue(red, green, blue, opacity); ctx.fillRect(x, yPadding + y, 1, 1); } } numRowsInPreviousStrip = numRowsInStrip; } } return canvas; };