Comparing version 2.1.1 to 2.2.0
{ | ||
"name": "exif-js", | ||
"version": "2.1.1", | ||
"version": "2.2.0", | ||
"homepage": "https://github.com/exif-js/exif-js", | ||
@@ -5,0 +5,0 @@ "authors": [ |
212
exif.js
@@ -168,2 +168,26 @@ (function() { | ||
// EXIF 2.3 Spec | ||
var IFD1Tags = EXIF.IFD1Tags = { | ||
0x0100: "ImageWidth", | ||
0x0101: "ImageHeight", | ||
0x0102: "BitsPerSample", | ||
0x0103: "Compression", | ||
0x0106: "PhotometricInterpretation", | ||
0x0111: "StripOffsets", | ||
0x0112: "Orientation", | ||
0x0115: "SamplesPerPixel", | ||
0x0116: "RowsPerStrip", | ||
0x0117: "StripByteCounts", | ||
0x011A: "XResolution", | ||
0x011B: "YResolution", | ||
0x011C: "PlanarConfiguration", | ||
0x0128: "ResolutionUnit", | ||
0x0201: "JpegIFOffset", // When image format is JPEG, this value show offset to JPEG data stored.(aka "ThumbnailOffset" or "JPEGInterchangeFormat") | ||
0x0202: "JpegIFByteCount", // When image format is JPEG, this value shows data size of JPEG image (aka "ThumbnailLength" or "JPEGInterchangeFormatLength") | ||
0x0211: "YCbCrCoefficients", | ||
0x0212: "YCbCrSubSampling", | ||
0x0213: "YCbCrPositioning", | ||
0x0214: "ReferenceBlackWhite" | ||
}; | ||
var StringValues = EXIF.StringValues = { | ||
@@ -349,4 +373,6 @@ ExposureProgram : { | ||
var iptcdata = findIPTCinJPEG(binFile); | ||
var xmpdata= findXMPinJPEG(binFile); | ||
img.exifdata = data || {}; | ||
img.iptcdata = iptcdata || {}; | ||
img.xmpdata = xmpdata || {}; | ||
if (callback) { | ||
@@ -384,3 +410,3 @@ callback.call(img); | ||
} | ||
} else if (window.FileReader && (img instanceof window.Blob || img instanceof window.File)) { | ||
} else if (self.FileReader && (img instanceof self.Blob || img instanceof self.File)) { | ||
var fileReader = new FileReader(); | ||
@@ -646,2 +672,70 @@ fileReader.onload = function(e) { | ||
/** | ||
* Given an IFD (Image File Directory) start offset | ||
* returns an offset to next IFD or 0 if it's the last IFD. | ||
*/ | ||
function getNextIFDOffset(dataView, dirStart, bigEnd){ | ||
//the first 2bytes means the number of directory entries contains in this IFD | ||
var entries = dataView.getUint16(dirStart, !bigEnd); | ||
// After last directory entry, there is a 4bytes of data, | ||
// it means an offset to next IFD. | ||
// If its value is '0x00000000', it means this is the last IFD and there is no linked IFD. | ||
return dataView.getUint32(dirStart + 2 + entries * 12, !bigEnd); // each entry is 12 bytes long | ||
} | ||
function readThumbnailImage(dataView, tiffStart, firstIFDOffset, bigEnd){ | ||
// get the IFD1 offset | ||
var IFD1OffsetPointer = getNextIFDOffset(dataView, tiffStart+firstIFDOffset, bigEnd); | ||
if (!IFD1OffsetPointer) { | ||
// console.log('******** IFD1Offset is empty, image thumb not found ********'); | ||
return {}; | ||
} | ||
else if (IFD1OffsetPointer > dataView.byteLength) { // this should not happen | ||
// console.log('******** IFD1Offset is outside the bounds of the DataView ********'); | ||
return {}; | ||
} | ||
// console.log('******* thumbnail IFD offset (IFD1) is: %s', IFD1OffsetPointer); | ||
var thumbTags = readTags(dataView, tiffStart, tiffStart + IFD1OffsetPointer, IFD1Tags, bigEnd) | ||
// EXIF 2.3 specification for JPEG format thumbnail | ||
// If the value of Compression(0x0103) Tag in IFD1 is '6', thumbnail image format is JPEG. | ||
// Most of Exif image uses JPEG format for thumbnail. In that case, you can get offset of thumbnail | ||
// by JpegIFOffset(0x0201) Tag in IFD1, size of thumbnail by JpegIFByteCount(0x0202) Tag. | ||
// Data format is ordinary JPEG format, starts from 0xFFD8 and ends by 0xFFD9. It seems that | ||
// JPEG format and 160x120pixels of size are recommended thumbnail format for Exif2.1 or later. | ||
if (thumbTags['Compression']) { | ||
// console.log('Thumbnail image found!'); | ||
switch (thumbTags['Compression']) { | ||
case 6: | ||
// console.log('Thumbnail image format is JPEG'); | ||
if (thumbTags.JpegIFOffset && thumbTags.JpegIFByteCount) { | ||
// extract the thumbnail | ||
var tOffset = tiffStart + thumbTags.JpegIFOffset; | ||
var tLength = thumbTags.JpegIFByteCount; | ||
thumbTags['blob'] = new Blob([new Uint8Array(dataView.buffer, tOffset, tLength)], { | ||
type: 'image/jpeg' | ||
}); | ||
} | ||
break; | ||
case 1: | ||
console.log("Thumbnail image format is TIFF, which is not implemented."); | ||
break; | ||
default: | ||
console.log("Unknown thumbnail image format '%s'", thumbTags['Compression']); | ||
} | ||
} | ||
else if (thumbTags['PhotometricInterpretation'] == 2) { | ||
console.log("Thumbnail image format is RGB, which is not implemented."); | ||
} | ||
return thumbTags; | ||
} | ||
function getStringFromDB(buffer, start, length) { | ||
@@ -744,7 +838,103 @@ var outstr = ""; | ||
// extract thumbnail | ||
tags['thumbnail'] = readThumbnailImage(file, tiffOffset, firstIFDOffset, bigEnd); | ||
return tags; | ||
} | ||
function findXMPinJPEG(file) { | ||
if (!('DOMParser' in self)) { | ||
// console.warn('XML parsing not supported without DOMParser'); | ||
return; | ||
} | ||
var dataView = new DataView(file); | ||
if (debug) console.log("Got file of length " + file.byteLength); | ||
if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { | ||
if (debug) console.log("Not a valid JPEG"); | ||
return false; // not a valid jpeg | ||
} | ||
var offset = 2, | ||
length = file.byteLength, | ||
dom = new DOMParser(); | ||
while (offset < (length-4)) { | ||
if (getStringFromDB(dataView, offset, 4) == "http") { | ||
var startOffset = offset - 1; | ||
var sectionLength = dataView.getUint16(offset - 2) - 1; | ||
var xmpString = getStringFromDB(dataView, startOffset, sectionLength) | ||
var xmpEndIndex = xmpString.indexOf('xmpmeta>') + 8; | ||
xmpString = xmpString.substring( xmpString.indexOf( '<x:xmpmeta' ), xmpEndIndex ); | ||
var indexOfXmp = xmpString.indexOf('x:xmpmeta') + 10 | ||
//Many custom written programs embed xmp/xml without any namespace. Following are some of them. | ||
//Without these namespaces, XML is thought to be invalid by parsers | ||
xmpString = xmpString.slice(0, indexOfXmp) | ||
+ 'xmlns:Iptc4xmpCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/" ' | ||
+ 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' | ||
+ 'xmlns:tiff="http://ns.adobe.com/tiff/1.0/" ' | ||
+ 'xmlns:plus="http://schemas.android.com/apk/lib/com.google.android.gms.plus" ' | ||
+ 'xmlns:ext="http://www.gettyimages.com/xsltExtension/1.0" ' | ||
+ 'xmlns:exif="http://ns.adobe.com/exif/1.0/" ' | ||
+ 'xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" ' | ||
+ 'xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" ' | ||
+ 'xmlns:crs="http://ns.adobe.com/camera-raw-settings/1.0/" ' | ||
+ 'xmlns:xapGImg="http://ns.adobe.com/xap/1.0/g/img/" ' | ||
+ 'xmlns:Iptc4xmpExt="http://iptc.org/std/Iptc4xmpExt/2008-02-29/" ' | ||
+ xmpString.slice(indexOfXmp) | ||
var domDocument = dom.parseFromString( xmpString, 'text/xml' ); | ||
return xml2Object(domDocument); | ||
} else{ | ||
offset++; | ||
} | ||
} | ||
} | ||
function xml2Object(xml) { | ||
try { | ||
var obj = {}; | ||
if (xml.children.length > 0) { | ||
for (var i = 0; i < xml.children.length; i++) { | ||
var item = xml.children.item(i); | ||
var attributes = item.attributes; | ||
for(var idx in attributes) { | ||
var itemAtt = attributes[idx]; | ||
var dataKey = itemAtt.nodeName; | ||
var dataValue = itemAtt.nodeValue; | ||
if(dataKey !== undefined) { | ||
obj[dataKey] = dataValue; | ||
} | ||
} | ||
var nodeName = item.nodeName; | ||
if (typeof (obj[nodeName]) == "undefined") { | ||
obj[nodeName] = xml2json(item); | ||
} else { | ||
if (typeof (obj[nodeName].push) == "undefined") { | ||
var old = obj[nodeName]; | ||
obj[nodeName] = []; | ||
obj[nodeName].push(old); | ||
} | ||
obj[nodeName].push(xml2json(item)); | ||
} | ||
} | ||
} else { | ||
obj = xml.textContent; | ||
} | ||
return obj; | ||
} catch (e) { | ||
console.log(e.message); | ||
} | ||
} | ||
EXIF.getData = function(img, callback) { | ||
if ((img instanceof Image || img instanceof HTMLImageElement) && !img.complete) return false; | ||
if ((self.Image && img instanceof self.Image) | ||
|| (self.HTMLImageElement && img instanceof self.HTMLImageElement) | ||
&& !img.complete) | ||
return false; | ||
@@ -765,2 +955,7 @@ if (!imageHasData(img)) { | ||
} | ||
EXIF.getIptcTag = function(img, tag) { | ||
if (!imageHasData(img)) return; | ||
return img.iptcdata[tag]; | ||
} | ||
@@ -779,2 +974,15 @@ EXIF.getAllTags = function(img) { | ||
} | ||
EXIF.getAllIptcTags = function(img) { | ||
if (!imageHasData(img)) return {}; | ||
var a, | ||
data = img.iptcdata, | ||
tags = {}; | ||
for (a in data) { | ||
if (data.hasOwnProperty(a)) { | ||
tags[a] = data[a]; | ||
} | ||
} | ||
return tags; | ||
} | ||
@@ -781,0 +989,0 @@ EXIF.pretty = function(img) { |
{ | ||
"name": "exif-js", | ||
"version": "2.1.1", | ||
"version": "2.2.0", | ||
"description": "JavaScript library for reading EXIF image metadata", | ||
@@ -5,0 +5,0 @@ "main": "exif.js", |
@@ -1,3 +0,86 @@ | ||
#Exif.js | ||
# Exif.js | ||
A JavaScript library for reading EXIF meta data from JPEG image files. | ||
A JavaScript library for reading [EXIF meta data](https://en.wikipedia.org/wiki/Exchangeable_image_file_format) from image files. | ||
You can use it on images in the browser, either from an image or a file input element. Both EXIF and IPTC metadata are retrieved. | ||
This package can also be used in AMD or CommonJS environments. | ||
**Note**: The EXIF standard applies only to `.jpg` and `.tiff` images. EXIF logic in this package is based on the EXIF standard v2.2 ([JEITA CP-3451, included in this repo](/spec/Exif2-2.pdf)). | ||
## Install | ||
Install `exif-js` through [NPM](https://www.npmjs.com/#getting-started): | ||
npm install exif-js --save | ||
Or [Bower](http://bower.io/): | ||
bower install exif-js --save | ||
Then add a `script` tag in your an HTML in the [best position](http://stackoverflow.com/questions/436411/where-is-the-best-place-to-put-script-tags-in-html-markup) referencing your local file. | ||
<script src="vendors/exif-js/exif-js"></script> | ||
**Note**: This repo has no `.min.js`. Do your own [minification](https://en.wikipedia.org/wiki/Minification_(programming)) if you want that. | ||
If you prefer another package manager you will probably manage :D. Or you can clone this GIT repository or download it's ZIP file and extract `exif.js` to your project. | ||
## Usage | ||
The package adds a global `EXIF` variable (or AMD or CommonJS equivalent). | ||
Start with calling the `EXIF.getData` function. You pass it an image as a parameter: | ||
- either an image from a `<img src="image.jpg">` | ||
- OR a user selected image in a `<file type="input">` element on your page. | ||
As a second parameter you specify a callback function. In the callback function you should use `this` to access the image with the aforementioned metadata you can then use as you want. | ||
That image now has an extra `exifdata` property which is a Javascript object with the EXIF metadata. You can access it's properties to get data like the *image caption*, the *date a photo was taken* or it's *orientation*. | ||
You can get all tages with `EXIF.getTag`. Or get a single tag with `EXIF.getTag`, where you specify the tag as the second parameter. | ||
The tag names to use are listed in `EXIF.Tags` in `exif.js`. | ||
**Important**: Note that you have to wait for the image to be completely loaded, before calling `getData` or any other function. It will silently fail otherwise. | ||
You can implement this wait, by running your exif-extracting logic on the `window.onLoad` function. Or on an image's own `onLoad` function. | ||
For jQuery users please note that you can NOT (reliably) use jQuery's `ready` event for this. Because it fires before images are loaded. | ||
You could use $(window).load() instead of $(document.ready() (please note that `exif-js has NO dependency on jQuery or any other external library). | ||
**JavaScript**: | ||
```javascript | ||
window.onload=getExif; | ||
function getExif() { | ||
var img1 = document.getElementById("img1"); | ||
EXIF.getData(img1, function() { | ||
var make = EXIF.getTag(this, "Make"); | ||
var model = EXIF.getTag(this, "Model"); | ||
var makeAndModel = document.getElementById("makeAndModel"); | ||
makeAndModel.innerHTML = `${make} ${model}`; | ||
}); | ||
var img2 = document.getElementById("img2"); | ||
EXIF.getData(img2, function() { | ||
var allMetaData = EXIF.getAllTags(this); | ||
var allMetaDataSpan = document.getElementById("allMetaDataSpan"); | ||
allMetaDataSpan.innerHTML = JSON.stringify(allMetaData, null, "\t"); | ||
}); | ||
} | ||
``` | ||
**HTML**: | ||
```html | ||
<img src="image1.jpg" id="img1" /> | ||
<pre>Make and model: <span id="makeAndModel"></span></div> | ||
<br/> | ||
<img src="image2.jpg" id="img2" /> | ||
<pre id="allMetaDataSpan"></pre> | ||
<br/> | ||
``` | ||
Note there are also alternate tags, such the `EXIF.TiffTags`. See the source code for the full definition and use. | ||
You can also get back a string with all the EXIF information in the image pretty printed by using `EXIF.pretty`. | ||
Check the included [example/index.html](/exif-js/exif-js/blob/master/example/index.html). | ||
Please refer to the [source code](exif.js) for more advanced usages such as getting image data from a [File/Blob](https://developer.mozilla.org/en/docs/Web/API/Blob) object (`EXIF.readFromBinaryFile`). | ||
## Contributions | ||
This is an [open source project](LICENSE.md). Please contribute by forking this repo and issueing a pull request. The project has had notable contributions already, like reading ITPC data. | ||
You can also contribute by [filing bugs or new features please issue](/exif-js/issues). | ||
Or improve the documentation. Please update this README when you do a pull request of proposed changes in base functionality. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
988507
14
930
87
1