├── LICENSE ├── README.md ├── test.html └── tiff.js /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TIFF.js # 2 | 3 | A JavaScript-based parser for the TIFF image format. 4 | 5 | ## Specification ## 6 | 7 | * http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf 8 | * http://partners.adobe.com/public/developer/en/tiff/TIFFPM6.pdf 9 | * http://partners.adobe.com/public/developer/en/tiff/TIFFphotoshop.pdf 10 | 11 | ## Metadata Tags ## 12 | 13 | * http://www.digitizationguidelines.gov/guidelines/TIFF_Metadata_Final.pdf 14 | * http://www.digitalpreservation.gov/formats/content/tiff_tags.shtml 15 | 16 | ## Sample Images ## 17 | 18 | * http://www.fileformat.info/format/tiff/sample/index.htm 19 | * http://www.remotesensing.org/libtiff/images.html 20 | * http://people.sc.fsu.edu/~jburkardt/data/tif/tif.html 21 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | TIFF test 9 | 10 | 46 | 47 | 48 | 49 |
50 | 51 | 52 | 53 | 54 |
55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /tiff.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | "use strict"; 6 | 7 | function TIFFParser() { 8 | this.tiffDataView = undefined; 9 | this.littleEndian = undefined; 10 | this.fileDirectories = []; 11 | }; 12 | 13 | TIFFParser.prototype = { 14 | isLittleEndian: function () { 15 | // Get byte order mark. 16 | var BOM = this.getBytes(2, 0); 17 | 18 | // Find out the endianness. 19 | if (BOM === 0x4949) { 20 | this.littleEndian = true; 21 | } else if (BOM === 0x4D4D) { 22 | this.littleEndian = false; 23 | } else { 24 | console.log( BOM ); 25 | throw TypeError("Invalid byte order value."); 26 | } 27 | 28 | return this.littleEndian; 29 | }, 30 | 31 | hasTowel: function () { 32 | // Check for towel. 33 | if (this.getBytes(2, 2) !== 42) { 34 | throw RangeError("You forgot your towel!"); 35 | return false; 36 | } 37 | 38 | return true; 39 | }, 40 | 41 | getFieldTagName: function (fieldTag) { 42 | // See: http://www.digitizationguidelines.gov/guidelines/TIFF_Metadata_Final.pdf 43 | // See: http://www.digitalpreservation.gov/formats/content/tiff_tags.shtml 44 | var fieldTagNames = { 45 | // TIFF Baseline 46 | 0x013B: 'Artist', 47 | 0x0102: 'BitsPerSample', 48 | 0x0109: 'CellLength', 49 | 0x0108: 'CellWidth', 50 | 0x0140: 'ColorMap', 51 | 0x0103: 'Compression', 52 | 0x8298: 'Copyright', 53 | 0x0132: 'DateTime', 54 | 0x0152: 'ExtraSamples', 55 | 0x010A: 'FillOrder', 56 | 0x0121: 'FreeByteCounts', 57 | 0x0120: 'FreeOffsets', 58 | 0x0123: 'GrayResponseCurve', 59 | 0x0122: 'GrayResponseUnit', 60 | 0x013C: 'HostComputer', 61 | 0x010E: 'ImageDescription', 62 | 0x0101: 'ImageLength', 63 | 0x0100: 'ImageWidth', 64 | 0x010F: 'Make', 65 | 0x0119: 'MaxSampleValue', 66 | 0x0118: 'MinSampleValue', 67 | 0x0110: 'Model', 68 | 0x00FE: 'NewSubfileType', 69 | 0x0112: 'Orientation', 70 | 0x0106: 'PhotometricInterpretation', 71 | 0x011C: 'PlanarConfiguration', 72 | 0x0128: 'ResolutionUnit', 73 | 0x0116: 'RowsPerStrip', 74 | 0x0115: 'SamplesPerPixel', 75 | 0x0131: 'Software', 76 | 0x0117: 'StripByteCounts', 77 | 0x0111: 'StripOffsets', 78 | 0x00FF: 'SubfileType', 79 | 0x0107: 'Threshholding', 80 | 0x011A: 'XResolution', 81 | 0x011B: 'YResolution', 82 | 83 | // TIFF Extended 84 | 0x0146: 'BadFaxLines', 85 | 0x0147: 'CleanFaxData', 86 | 0x0157: 'ClipPath', 87 | 0x0148: 'ConsecutiveBadFaxLines', 88 | 0x01B1: 'Decode', 89 | 0x01B2: 'DefaultImageColor', 90 | 0x010D: 'DocumentName', 91 | 0x0150: 'DotRange', 92 | 0x0141: 'HalftoneHints', 93 | 0x015A: 'Indexed', 94 | 0x015B: 'JPEGTables', 95 | 0x011D: 'PageName', 96 | 0x0129: 'PageNumber', 97 | 0x013D: 'Predictor', 98 | 0x013F: 'PrimaryChromaticities', 99 | 0x0214: 'ReferenceBlackWhite', 100 | 0x0153: 'SampleFormat', 101 | 0x022F: 'StripRowCounts', 102 | 0x014A: 'SubIFDs', 103 | 0x0124: 'T4Options', 104 | 0x0125: 'T6Options', 105 | 0x0145: 'TileByteCounts', 106 | 0x0143: 'TileLength', 107 | 0x0144: 'TileOffsets', 108 | 0x0142: 'TileWidth', 109 | 0x012D: 'TransferFunction', 110 | 0x013E: 'WhitePoint', 111 | 0x0158: 'XClipPathUnits', 112 | 0x011E: 'XPosition', 113 | 0x0211: 'YCbCrCoefficients', 114 | 0x0213: 'YCbCrPositioning', 115 | 0x0212: 'YCbCrSubSampling', 116 | 0x0159: 'YClipPathUnits', 117 | 0x011F: 'YPosition', 118 | 119 | // EXIF 120 | 0x9202: 'ApertureValue', 121 | 0xA001: 'ColorSpace', 122 | 0x9004: 'DateTimeDigitized', 123 | 0x9003: 'DateTimeOriginal', 124 | 0x8769: 'Exif IFD', 125 | 0x9000: 'ExifVersion', 126 | 0x829A: 'ExposureTime', 127 | 0xA300: 'FileSource', 128 | 0x9209: 'Flash', 129 | 0xA000: 'FlashpixVersion', 130 | 0x829D: 'FNumber', 131 | 0xA420: 'ImageUniqueID', 132 | 0x9208: 'LightSource', 133 | 0x927C: 'MakerNote', 134 | 0x9201: 'ShutterSpeedValue', 135 | 0x9286: 'UserComment', 136 | 137 | // IPTC 138 | 0x83BB: 'IPTC', 139 | 140 | // ICC 141 | 0x8773: 'ICC Profile', 142 | 143 | // XMP 144 | 0x02BC: 'XMP', 145 | 146 | // GDAL 147 | 0xA480: 'GDAL_METADATA', 148 | 0xA481: 'GDAL_NODATA', 149 | 150 | // Photoshop 151 | 0x8649: 'Photoshop', 152 | }; 153 | 154 | var fieldTagName; 155 | 156 | if (fieldTag in fieldTagNames) { 157 | fieldTagName = fieldTagNames[fieldTag]; 158 | } else { 159 | console.log( "Unknown Field Tag:", fieldTag); 160 | fieldTagName = "Tag" + fieldTag; 161 | } 162 | 163 | return fieldTagName; 164 | }, 165 | 166 | getFieldTypeName: function (fieldType) { 167 | var fieldTypeNames = { 168 | 0x0001: 'BYTE', 169 | 0x0002: 'ASCII', 170 | 0x0003: 'SHORT', 171 | 0x0004: 'LONG', 172 | 0x0005: 'RATIONAL', 173 | 0x0006: 'SBYTE', 174 | 0x0007: 'UNDEFINED', 175 | 0x0008: 'SSHORT', 176 | 0x0009: 'SLONG', 177 | 0x000A: 'SRATIONAL', 178 | 0x000B: 'FLOAT', 179 | 0x000C: 'DOUBLE', 180 | }; 181 | 182 | var fieldTypeName; 183 | 184 | if (fieldType in fieldTypeNames) { 185 | fieldTypeName = fieldTypeNames[fieldType]; 186 | } 187 | 188 | return fieldTypeName; 189 | }, 190 | 191 | getFieldTypeLength: function (fieldTypeName) { 192 | var fieldTypeLength; 193 | 194 | if (['BYTE', 'ASCII', 'SBYTE', 'UNDEFINED'].indexOf(fieldTypeName) !== -1) { 195 | fieldTypeLength = 1; 196 | } else if (['SHORT', 'SSHORT'].indexOf(fieldTypeName) !== -1) { 197 | fieldTypeLength = 2; 198 | } else if (['LONG', 'SLONG', 'FLOAT'].indexOf(fieldTypeName) !== -1) { 199 | fieldTypeLength = 4; 200 | } else if (['RATIONAL', 'SRATIONAL', 'DOUBLE'].indexOf(fieldTypeName) !== -1) { 201 | fieldTypeLength = 8; 202 | } 203 | 204 | return fieldTypeLength; 205 | }, 206 | 207 | getBits: function (numBits, byteOffset, bitOffset) { 208 | bitOffset = bitOffset || 0; 209 | var extraBytes = Math.floor(bitOffset / 8); 210 | var newByteOffset = byteOffset + extraBytes; 211 | var totalBits = bitOffset + numBits; 212 | var shiftRight = 32 - numBits; 213 | 214 | if (totalBits <= 0) { 215 | console.log( numBits, byteOffset, bitOffset ); 216 | throw RangeError("No bits requested"); 217 | } else if (totalBits <= 8) { 218 | var shiftLeft = 24 + bitOffset; 219 | var rawBits = this.tiffDataView.getUint8(newByteOffset, this.littleEndian); 220 | } else if (totalBits <= 16) { 221 | var shiftLeft = 16 + bitOffset; 222 | var rawBits = this.tiffDataView.getUint16(newByteOffset, this.littleEndian); 223 | } else if (totalBits <= 32) { 224 | var shiftLeft = bitOffset; 225 | var rawBits = this.tiffDataView.getUint32(newByteOffset, this.littleEndian); 226 | } else { 227 | console.log( numBits, byteOffset, bitOffset ); 228 | throw RangeError("Too many bits requested"); 229 | } 230 | 231 | var chunkInfo = { 232 | 'bits': ((rawBits << shiftLeft) >>> shiftRight), 233 | 'byteOffset': newByteOffset + Math.floor(totalBits / 8), 234 | 'bitOffset': totalBits % 8, 235 | }; 236 | 237 | return chunkInfo; 238 | }, 239 | 240 | getBytes: function (numBytes, offset) { 241 | if (numBytes <= 0) { 242 | console.log( numBytes, offset ); 243 | throw RangeError("No bytes requested"); 244 | } else if (numBytes <= 1) { 245 | return this.tiffDataView.getUint8(offset, this.littleEndian); 246 | } else if (numBytes <= 2) { 247 | return this.tiffDataView.getUint16(offset, this.littleEndian); 248 | } else if (numBytes <= 3) { 249 | return this.tiffDataView.getUint32(offset, this.littleEndian) >>> 8; 250 | } else if (numBytes <= 4) { 251 | return this.tiffDataView.getUint32(offset, this.littleEndian); 252 | } else { 253 | console.log( numBytes, offset ); 254 | throw RangeError("Too many bytes requested"); 255 | } 256 | }, 257 | 258 | getFieldValues: function (fieldTagName, fieldTypeName, typeCount, valueOffset) { 259 | var fieldValues = []; 260 | 261 | var fieldTypeLength = this.getFieldTypeLength(fieldTypeName); 262 | var fieldValueSize = fieldTypeLength * typeCount; 263 | 264 | if (fieldValueSize <= 4) { 265 | // The value is stored at the big end of the valueOffset. 266 | if (this.littleEndian === false) { 267 | var value = valueOffset >>> ((4 - fieldTypeLength) * 8); 268 | } else { 269 | var value = valueOffset; 270 | } 271 | 272 | fieldValues.push(value); 273 | } else { 274 | for (var i = 0; i < typeCount; i++) { 275 | var indexOffset = fieldTypeLength * i; 276 | 277 | if (fieldTypeLength >= 8) { 278 | if (['RATIONAL', 'SRATIONAL'].indexOf(fieldTypeName) !== -1) { 279 | // Numerator 280 | fieldValues.push(this.getBytes(4, valueOffset + indexOffset)); 281 | // Denominator 282 | fieldValues.push(this.getBytes(4, valueOffset + indexOffset + 4)); 283 | // } else if (['DOUBLE'].indexOf(fieldTypeName) !== -1) { 284 | // fieldValues.push(this.getBytes(4, valueOffset + indexOffset) + this.getBytes(4, valueOffset + indexOffset + 4)); 285 | } else { 286 | console.log( fieldTypeName, typeCount, fieldValueSize ); 287 | throw TypeError("Can't handle this field type or size"); 288 | } 289 | } else { 290 | fieldValues.push(this.getBytes(fieldTypeLength, valueOffset + indexOffset)); 291 | } 292 | } 293 | } 294 | 295 | if (fieldTypeName === 'ASCII') { 296 | fieldValues.forEach(function(e, i, a) { a[i] = String.fromCharCode(e); }); 297 | } 298 | 299 | return fieldValues; 300 | }, 301 | 302 | clampColorSample: function(colorSample, bitsPerSample) { 303 | var multiplier = Math.pow(2, 8 - bitsPerSample); 304 | 305 | return Math.floor((colorSample * multiplier) + (multiplier - 1)); 306 | }, 307 | 308 | makeRGBAFillValue: function(r, g, b, a) { 309 | if(typeof a === 'undefined') { 310 | a = 1.0; 311 | } 312 | return "rgba(" + r + ", " + g + ", " + b + ", " + a + ")"; 313 | }, 314 | 315 | parseFileDirectory: function (byteOffset) { 316 | var numDirEntries = this.getBytes(2, byteOffset); 317 | 318 | var tiffFields = []; 319 | 320 | for (var i = byteOffset + 2, entryCount = 0; entryCount < numDirEntries; i += 12, entryCount++) { 321 | var fieldTag = this.getBytes(2, i); 322 | var fieldType = this.getBytes(2, i + 2); 323 | var typeCount = this.getBytes(4, i + 4); 324 | var valueOffset = this.getBytes(4, i + 8); 325 | 326 | var fieldTagName = this.getFieldTagName( fieldTag ); 327 | var fieldTypeName = this.getFieldTypeName( fieldType ); 328 | 329 | var fieldValues = this.getFieldValues(fieldTagName, fieldTypeName, typeCount, valueOffset); 330 | 331 | tiffFields[fieldTagName] = { 'type': fieldTypeName, 'values': fieldValues }; 332 | } 333 | 334 | this.fileDirectories.push( tiffFields ); 335 | 336 | var nextIFDByteOffset = this.getBytes(4, i); 337 | 338 | if (nextIFDByteOffset === 0x00000000) { 339 | return this.fileDirectories; 340 | } else { 341 | return this.parseFileDirectory(nextIFDByteOffset); 342 | } 343 | }, 344 | 345 | parseTIFF: function (tiffArrayBuffer, canvas) { 346 | canvas = canvas || document.createElement('canvas'); 347 | 348 | this.tiffDataView = new DataView(tiffArrayBuffer); 349 | this.canvas = canvas; 350 | 351 | this.littleEndian = this.isLittleEndian(this.tiffDataView); 352 | 353 | if (!this.hasTowel(this.tiffDataView, this.littleEndian)) { 354 | return; 355 | } 356 | 357 | var firstIFDByteOffset = this.getBytes(4, 4); 358 | 359 | this.fileDirectories = this.parseFileDirectory(firstIFDByteOffset); 360 | 361 | var fileDirectory = this.fileDirectories[0]; 362 | 363 | console.log( fileDirectory ); 364 | 365 | var imageWidth = fileDirectory.ImageWidth.values[0]; 366 | var imageLength = fileDirectory.ImageLength.values[0]; 367 | 368 | this.canvas.width = imageWidth; 369 | this.canvas.height = imageLength; 370 | 371 | var strips = []; 372 | 373 | var compression = (fileDirectory.Compression) ? fileDirectory.Compression.values[0] : 1; 374 | 375 | var samplesPerPixel = fileDirectory.SamplesPerPixel.values[0]; 376 | 377 | var sampleProperties = []; 378 | 379 | var bitsPerPixel = 0; 380 | var hasBytesPerPixel = false; 381 | 382 | fileDirectory.BitsPerSample.values.forEach(function(bitsPerSample, i, bitsPerSampleValues) { 383 | sampleProperties[i] = { 384 | 'bitsPerSample': bitsPerSample, 385 | 'hasBytesPerSample': false, 386 | 'bytesPerSample': undefined, 387 | }; 388 | 389 | if ((bitsPerSample % 8) === 0) { 390 | sampleProperties[i].hasBytesPerSample = true; 391 | sampleProperties[i].bytesPerSample = bitsPerSample / 8; 392 | } 393 | 394 | bitsPerPixel += bitsPerSample; 395 | }, this); 396 | 397 | if ((bitsPerPixel % 8) === 0) { 398 | hasBytesPerPixel = true; 399 | var bytesPerPixel = bitsPerPixel / 8; 400 | } 401 | 402 | var stripOffsetValues = fileDirectory.StripOffsets.values; 403 | var numStripOffsetValues = stripOffsetValues.length; 404 | 405 | // StripByteCounts is supposed to be required, but see if we can recover anyway. 406 | if (fileDirectory.StripByteCounts) { 407 | var stripByteCountValues = fileDirectory.StripByteCounts.values; 408 | } else { 409 | console.log("Missing StripByteCounts!"); 410 | 411 | // Infer StripByteCounts, if possible. 412 | if (numStripOffsetValues === 1) { 413 | var stripByteCountValues = [Math.ceil((imageWidth * imageLength * bitsPerPixel) / 8)]; 414 | } else { 415 | throw Error("Cannot recover from missing StripByteCounts"); 416 | } 417 | } 418 | 419 | // Loop through strips and decompress as necessary. 420 | for (var i = 0; i < numStripOffsetValues; i++) { 421 | var stripOffset = stripOffsetValues[i]; 422 | strips[i] = []; 423 | 424 | var stripByteCount = stripByteCountValues[i]; 425 | 426 | // Loop through pixels. 427 | for (var byteOffset = 0, bitOffset = 0, jIncrement = 1, getHeader = true, pixel = [], numBytes = 0, sample = 0, currentSample = 0; byteOffset < stripByteCount; byteOffset += jIncrement) { 428 | // Decompress strip. 429 | switch (compression) { 430 | // Uncompressed 431 | case 1: 432 | // Loop through samples (sub-pixels). 433 | for (var m = 0, pixel = []; m < samplesPerPixel; m++) { 434 | if (sampleProperties[m].hasBytesPerSample) { 435 | // XXX: This is wrong! 436 | var sampleOffset = sampleProperties[m].bytesPerSample * m; 437 | 438 | pixel.push(this.getBytes(sampleProperties[m].bytesPerSample, stripOffset + byteOffset + sampleOffset)); 439 | } else { 440 | var sampleInfo = this.getBits(sampleProperties[m].bitsPerSample, stripOffset + byteOffset, bitOffset); 441 | 442 | pixel.push(sampleInfo.bits); 443 | 444 | byteOffset = sampleInfo.byteOffset - stripOffset; 445 | bitOffset = sampleInfo.bitOffset; 446 | 447 | throw RangeError("Cannot handle sub-byte bits per sample"); 448 | } 449 | } 450 | 451 | strips[i].push(pixel); 452 | 453 | if (hasBytesPerPixel) { 454 | jIncrement = bytesPerPixel; 455 | } else { 456 | jIncrement = 0; 457 | 458 | throw RangeError("Cannot handle sub-byte bits per pixel"); 459 | } 460 | break; 461 | 462 | // CITT Group 3 1-Dimensional Modified Huffman run-length encoding 463 | case 2: 464 | // XXX: Use PDF.js code? 465 | break; 466 | 467 | // Group 3 Fax 468 | case 3: 469 | // XXX: Use PDF.js code? 470 | break; 471 | 472 | // Group 4 Fax 473 | case 4: 474 | // XXX: Use PDF.js code? 475 | break; 476 | 477 | // LZW 478 | case 5: 479 | // XXX: Use PDF.js code? 480 | break; 481 | 482 | // Old-style JPEG (TIFF 6.0) 483 | case 6: 484 | // XXX: Use PDF.js code? 485 | break; 486 | 487 | // New-style JPEG (TIFF Specification Supplement 2) 488 | case 7: 489 | // XXX: Use PDF.js code? 490 | break; 491 | 492 | // PackBits 493 | case 32773: 494 | // Are we ready for a new block? 495 | if (getHeader) { 496 | getHeader = false; 497 | 498 | var blockLength = 1; 499 | var iterations = 1; 500 | 501 | // The header byte is signed. 502 | var header = this.tiffDataView.getInt8(stripOffset + byteOffset, this.littleEndian); 503 | 504 | if ((header >= 0) && (header <= 127)) { // Normal pixels. 505 | blockLength = header + 1; 506 | } else if ((header >= -127) && (header <= -1)) { // Collapsed pixels. 507 | iterations = -header + 1; 508 | } else /*if (header === -128)*/ { // Placeholder byte? 509 | getHeader = true; 510 | } 511 | } else { 512 | var currentByte = this.getBytes(1, stripOffset + byteOffset); 513 | 514 | // Duplicate bytes, if necessary. 515 | for (var m = 0; m < iterations; m++) { 516 | if (sampleProperties[sample].hasBytesPerSample) { 517 | // We're reading one byte at a time, so we need to handle multi-byte samples. 518 | currentSample = (currentSample << (8 * numBytes)) | currentByte; 519 | numBytes++; 520 | 521 | // Is our sample complete? 522 | if (numBytes === sampleProperties[sample].bytesPerSample) { 523 | pixel.push(currentSample); 524 | currentSample = numBytes = 0; 525 | sample++; 526 | } 527 | } else { 528 | throw RangeError("Cannot handle sub-byte bits per sample"); 529 | } 530 | 531 | // Is our pixel complete? 532 | if (sample === samplesPerPixel) 533 | { 534 | strips[i].push(pixel); 535 | 536 | pixel = []; 537 | sample = 0; 538 | } 539 | } 540 | 541 | blockLength--; 542 | 543 | // Is our block complete? 544 | if (blockLength === 0) { 545 | getHeader = true; 546 | } 547 | } 548 | 549 | jIncrement = 1; 550 | break; 551 | 552 | // Unknown compression algorithm 553 | default: 554 | // Do not attempt to parse the image data. 555 | break; 556 | } 557 | } 558 | 559 | // console.log( strips[i] ); 560 | } 561 | 562 | // console.log( strips ); 563 | 564 | if (canvas.getContext) { 565 | var ctx = this.canvas.getContext("2d"); 566 | 567 | // Set a default fill style. 568 | ctx.fillStyle = this.makeRGBAFillValue(255, 255, 255, 0); 569 | 570 | // If RowsPerStrip is missing, the whole image is in one strip. 571 | if (fileDirectory.RowsPerStrip) { 572 | var rowsPerStrip = fileDirectory.RowsPerStrip.values[0]; 573 | } else { 574 | var rowsPerStrip = imageLength; 575 | } 576 | 577 | var numStrips = strips.length; 578 | 579 | var imageLengthModRowsPerStrip = imageLength % rowsPerStrip; 580 | var rowsInLastStrip = (imageLengthModRowsPerStrip === 0) ? rowsPerStrip : imageLengthModRowsPerStrip; 581 | 582 | var numRowsInStrip = rowsPerStrip; 583 | var numRowsInPreviousStrip = 0; 584 | 585 | var photometricInterpretation = fileDirectory.PhotometricInterpretation.values[0]; 586 | 587 | var extraSamplesValues = []; 588 | var numExtraSamples = 0; 589 | 590 | if (fileDirectory.ExtraSamples) { 591 | extraSamplesValues = fileDirectory.ExtraSamples.values; 592 | numExtraSamples = extraSamplesValues.length; 593 | } 594 | 595 | if (fileDirectory.ColorMap) { 596 | var colorMapValues = fileDirectory.ColorMap.values; 597 | var colorMapSampleSize = Math.pow(2, sampleProperties[0].bitsPerSample); 598 | } 599 | 600 | // Loop through the strips in the image. 601 | for (var i = 0; i < numStrips; i++) { 602 | // The last strip may be short. 603 | if ((i + 1) === numStrips) { 604 | numRowsInStrip = rowsInLastStrip; 605 | } 606 | 607 | var numPixels = strips[i].length; 608 | var yPadding = numRowsInPreviousStrip * i; 609 | 610 | // Loop through the rows in the strip. 611 | for (var y = 0, j = 0; y < numRowsInStrip, j < numPixels; y++) { 612 | // Loop through the pixels in the row. 613 | for (var x = 0; x < imageWidth; x++, j++) { 614 | var pixelSamples = strips[i][j]; 615 | 616 | var red = 0; 617 | var green = 0; 618 | var blue = 0; 619 | var opacity = 1.0; 620 | 621 | if (numExtraSamples > 0) { 622 | for (var k = 0; k < numExtraSamples; k++) { 623 | if (extraSamplesValues[k] === 1 || extraSamplesValues[k] === 2) { 624 | // Clamp opacity to the range [0,1]. 625 | opacity = pixelSamples[3 + k] / 256; 626 | 627 | break; 628 | } 629 | } 630 | } 631 | 632 | switch (photometricInterpretation) { 633 | // Bilevel or Grayscale 634 | // WhiteIsZero 635 | case 0: 636 | if (sampleProperties[0].hasBytesPerSample) { 637 | var invertValue = Math.pow(0x10, sampleProperties[0].bytesPerSample * 2); 638 | } 639 | 640 | // Invert samples. 641 | pixelSamples.forEach(function(sample, index, samples) { samples[index] = invertValue - sample; }); 642 | 643 | // Bilevel or Grayscale 644 | // BlackIsZero 645 | case 1: 646 | red = green = blue = this.clampColorSample(pixelSamples[0], sampleProperties[0].bitsPerSample); 647 | break; 648 | 649 | // RGB Full Color 650 | case 2: 651 | red = this.clampColorSample(pixelSamples[0], sampleProperties[0].bitsPerSample); 652 | green = this.clampColorSample(pixelSamples[1], sampleProperties[1].bitsPerSample); 653 | blue = this.clampColorSample(pixelSamples[2], sampleProperties[2].bitsPerSample); 654 | break; 655 | 656 | // RGB Color Palette 657 | case 3: 658 | if (colorMapValues === undefined) { 659 | throw Error("Palette image missing color map"); 660 | } 661 | 662 | var colorMapIndex = pixelSamples[0]; 663 | 664 | red = this.clampColorSample(colorMapValues[colorMapIndex], 16); 665 | green = this.clampColorSample(colorMapValues[colorMapSampleSize + colorMapIndex], 16); 666 | blue = this.clampColorSample(colorMapValues[(2 * colorMapSampleSize) + colorMapIndex], 16); 667 | break; 668 | 669 | // Transparency mask 670 | case 4: 671 | throw RangeError( 'Not Yet Implemented: Transparency mask' ); 672 | break; 673 | 674 | // CMYK 675 | case 5: 676 | throw RangeError( 'Not Yet Implemented: CMYK' ); 677 | break; 678 | 679 | // YCbCr 680 | case 6: 681 | throw RangeError( 'Not Yet Implemented: YCbCr' ); 682 | break; 683 | 684 | // CIELab 685 | case 8: 686 | throw RangeError( 'Not Yet Implemented: CIELab' ); 687 | break; 688 | 689 | // Unknown Photometric Interpretation 690 | default: 691 | throw RangeError( 'Unknown Photometric Interpretation:', photometricInterpretation ); 692 | break; 693 | } 694 | 695 | ctx.fillStyle = this.makeRGBAFillValue(red, green, blue, opacity); 696 | ctx.fillRect(x, yPadding + y, 1, 1); 697 | } 698 | } 699 | 700 | numRowsInPreviousStrip = numRowsInStrip; 701 | } 702 | } 703 | 704 | /* for (var i = 0, numFileDirectories = this.fileDirectories.length; i < numFileDirectories; i++) { 705 | // Stuff 706 | }*/ 707 | 708 | return this.canvas; 709 | }, 710 | } 711 | --------------------------------------------------------------------------------