├── 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 |
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 |
--------------------------------------------------------------------------------