123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- /*
- * JavaScript Load Image Exif Parser
- * https://github.com/blueimp/JavaScript-Load-Image
- *
- * Copyright 2013, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * https://opensource.org/licenses/MIT
- */
- /* global define */
- ;(function (factory) {
- 'use strict'
- if (typeof define === 'function' && define.amd) {
- // Register as an anonymous AMD module:
- define(['./load-image', './load-image-meta'], factory)
- } else if (typeof module === 'object' && module.exports) {
- factory(require('./load-image'), require('./load-image-meta'))
- } else {
- // Browser globals:
- factory(window.loadImage)
- }
- }(function (loadImage) {
- 'use strict'
- loadImage.ExifMap = function () {
- return this
- }
- loadImage.ExifMap.prototype.map = {
- 'Orientation': 0x0112
- }
- loadImage.ExifMap.prototype.get = function (id) {
- return this[id] || this[this.map[id]]
- }
- loadImage.getExifThumbnail = function (dataView, offset, length) {
- var hexData,
- i,
- b
- if (!length || offset + length > dataView.byteLength) {
- console.log('Invalid Exif data: Invalid thumbnail data.')
- return
- }
- hexData = []
- for (i = 0; i < length; i += 1) {
- b = dataView.getUint8(offset + i)
- hexData.push((b < 16 ? '0' : '') + b.toString(16))
- }
- return 'data:image/jpeg,%' + hexData.join('%')
- }
- loadImage.exifTagTypes = {
- // byte, 8-bit unsigned int:
- 1: {
- getValue: function (dataView, dataOffset) {
- return dataView.getUint8(dataOffset)
- },
- size: 1
- },
- // ascii, 8-bit byte:
- 2: {
- getValue: function (dataView, dataOffset) {
- return String.fromCharCode(dataView.getUint8(dataOffset))
- },
- size: 1,
- ascii: true
- },
- // short, 16 bit int:
- 3: {
- getValue: function (dataView, dataOffset, littleEndian) {
- return dataView.getUint16(dataOffset, littleEndian)
- },
- size: 2
- },
- // long, 32 bit int:
- 4: {
- getValue: function (dataView, dataOffset, littleEndian) {
- return dataView.getUint32(dataOffset, littleEndian)
- },
- size: 4
- },
- // rational = two long values, first is numerator, second is denominator:
- 5: {
- getValue: function (dataView, dataOffset, littleEndian) {
- return dataView.getUint32(dataOffset, littleEndian) /
- dataView.getUint32(dataOffset + 4, littleEndian)
- },
- size: 8
- },
- // slong, 32 bit signed int:
- 9: {
- getValue: function (dataView, dataOffset, littleEndian) {
- return dataView.getInt32(dataOffset, littleEndian)
- },
- size: 4
- },
- // srational, two slongs, first is numerator, second is denominator:
- 10: {
- getValue: function (dataView, dataOffset, littleEndian) {
- return dataView.getInt32(dataOffset, littleEndian) /
- dataView.getInt32(dataOffset + 4, littleEndian)
- },
- size: 8
- }
- }
- // undefined, 8-bit byte, value depending on field:
- loadImage.exifTagTypes[7] = loadImage.exifTagTypes[1]
- loadImage.getExifValue = function (dataView, tiffOffset, offset, type, length, littleEndian) {
- var tagType = loadImage.exifTagTypes[type]
- var tagSize
- var dataOffset
- var values
- var i
- var str
- var c
- if (!tagType) {
- console.log('Invalid Exif data: Invalid tag type.')
- return
- }
- tagSize = tagType.size * length
- // Determine if the value is contained in the dataOffset bytes,
- // or if the value at the dataOffset is a pointer to the actual data:
- dataOffset = tagSize > 4
- ? tiffOffset + dataView.getUint32(offset + 8, littleEndian)
- : (offset + 8)
- if (dataOffset + tagSize > dataView.byteLength) {
- console.log('Invalid Exif data: Invalid data offset.')
- return
- }
- if (length === 1) {
- return tagType.getValue(dataView, dataOffset, littleEndian)
- }
- values = []
- for (i = 0; i < length; i += 1) {
- values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian)
- }
- if (tagType.ascii) {
- str = ''
- // Concatenate the chars:
- for (i = 0; i < values.length; i += 1) {
- c = values[i]
- // Ignore the terminating NULL byte(s):
- if (c === '\u0000') {
- break
- }
- str += c
- }
- return str
- }
- return values
- }
- loadImage.parseExifTag = function (dataView, tiffOffset, offset, littleEndian, data) {
- var tag = dataView.getUint16(offset, littleEndian)
- data.exif[tag] = loadImage.getExifValue(
- dataView,
- tiffOffset,
- offset,
- dataView.getUint16(offset + 2, littleEndian), // tag type
- dataView.getUint32(offset + 4, littleEndian), // tag length
- littleEndian
- )
- }
- loadImage.parseExifTags = function (dataView, tiffOffset, dirOffset, littleEndian, data) {
- var tagsNumber,
- dirEndOffset,
- i
- if (dirOffset + 6 > dataView.byteLength) {
- console.log('Invalid Exif data: Invalid directory offset.')
- return
- }
- tagsNumber = dataView.getUint16(dirOffset, littleEndian)
- dirEndOffset = dirOffset + 2 + 12 * tagsNumber
- if (dirEndOffset + 4 > dataView.byteLength) {
- console.log('Invalid Exif data: Invalid directory size.')
- return
- }
- for (i = 0; i < tagsNumber; i += 1) {
- this.parseExifTag(
- dataView,
- tiffOffset,
- dirOffset + 2 + 12 * i, // tag offset
- littleEndian,
- data
- )
- }
- // Return the offset to the next directory:
- return dataView.getUint32(dirEndOffset, littleEndian)
- }
- loadImage.parseExifData = function (dataView, offset, length, data, options) {
- if (options.disableExif) {
- return
- }
- var tiffOffset = offset + 10
- var littleEndian
- var dirOffset
- var thumbnailData
- // Check for the ASCII code for "Exif" (0x45786966):
- if (dataView.getUint32(offset + 4) !== 0x45786966) {
- // No Exif data, might be XMP data instead
- return
- }
- if (tiffOffset + 8 > dataView.byteLength) {
- console.log('Invalid Exif data: Invalid segment size.')
- return
- }
- // Check for the two null bytes:
- if (dataView.getUint16(offset + 8) !== 0x0000) {
- console.log('Invalid Exif data: Missing byte alignment offset.')
- return
- }
- // Check the byte alignment:
- switch (dataView.getUint16(tiffOffset)) {
- case 0x4949:
- littleEndian = true
- break
- case 0x4D4D:
- littleEndian = false
- break
- default:
- console.log('Invalid Exif data: Invalid byte alignment marker.')
- return
- }
- // Check for the TIFF tag marker (0x002A):
- if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
- console.log('Invalid Exif data: Missing TIFF marker.')
- return
- }
- // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
- dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian)
- // Create the exif object to store the tags:
- data.exif = new loadImage.ExifMap()
- // Parse the tags of the main image directory and retrieve the
- // offset to the next directory, usually the thumbnail directory:
- dirOffset = loadImage.parseExifTags(
- dataView,
- tiffOffset,
- tiffOffset + dirOffset,
- littleEndian,
- data
- )
- if (dirOffset && !options.disableExifThumbnail) {
- thumbnailData = {exif: {}}
- dirOffset = loadImage.parseExifTags(
- dataView,
- tiffOffset,
- tiffOffset + dirOffset,
- littleEndian,
- thumbnailData
- )
- // Check for JPEG Thumbnail offset:
- if (thumbnailData.exif[0x0201]) {
- data.exif.Thumbnail = loadImage.getExifThumbnail(
- dataView,
- tiffOffset + thumbnailData.exif[0x0201],
- thumbnailData.exif[0x0202] // Thumbnail data length
- )
- }
- }
- // Check for Exif Sub IFD Pointer:
- if (data.exif[0x8769] && !options.disableExifSub) {
- loadImage.parseExifTags(
- dataView,
- tiffOffset,
- tiffOffset + data.exif[0x8769], // directory offset
- littleEndian,
- data
- )
- }
- // Check for GPS Info IFD Pointer:
- if (data.exif[0x8825] && !options.disableExifGps) {
- loadImage.parseExifTags(
- dataView,
- tiffOffset,
- tiffOffset + data.exif[0x8825], // directory offset
- littleEndian,
- data
- )
- }
- }
- // Registers the Exif parser for the APP1 JPEG meta data segment:
- loadImage.metaDataParsers.jpeg[0xffe1].push(loadImage.parseExifData)
- // Adds the following properties to the parseMetaData callback data:
- // * exif: The exif tags, parsed by the parseExifData method
- // Adds the following options to the parseMetaData method:
- // * disableExif: Disables Exif parsing.
- // * disableExifThumbnail: Disables parsing of the Exif Thumbnail.
- // * disableExifSub: Disables parsing of the Exif Sub IFD.
- // * disableExifGps: Disables parsing of the Exif GPS Info IFD.
- }))
|