Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@eris/exif

Package Overview
Dependencies
Maintainers
1
Versions
109
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@eris/exif - npm Package Compare versions

Comparing version 0.4.2-alpha.14 to 0.4.2-alpha.15

4

dist/decoder/ifd-entry.d.ts
/// <reference types="node" />
import { IIFDEntry, IReader, IFDTagName, Endian } from '../utils/types';
import { IIFDEntry, IReader, IFDTagName, Endian, IBufferLike } from '../utils/types';
export declare class IFDEntry implements IIFDEntry {

@@ -12,3 +12,3 @@ startOffset: number;

readonly friendlyTagName: IFDTagName;
getValue(reader?: IReader): string | number;
getValue(reader?: IReader): string | number | IBufferLike;
getReader(reader?: IReader): IReader;

@@ -15,0 +15,0 @@ static read(reader: IReader): IFDEntry;

@@ -53,3 +53,3 @@ "use strict";

case types_1.IFDDataType.Unknown:
return '';
return entryReader.getBuffer();
default:

@@ -56,0 +56,0 @@ throw new TypeError(`Unsupported data type (${this.dataType}) for tag (${this.tag})`);

@@ -25,4 +25,5 @@ import { IBufferLike, IGenericMetadata } from '../utils/types';

static isJPEG(buffer: IBufferLike): boolean;
static isLikelyJPEG(buffer: IBufferLike): boolean;
static injectEXIFMetadata(jpegBuffer: IBufferLike, exifBuffer: IBufferLike): IBufferLike;
static injectXMPMetadata(jpegBuffer: IBufferLike, xmpBuffer: IBufferLike): IBufferLike;
}

@@ -8,2 +8,4 @@ "use strict";

const xmp_decoder_1 = require("./xmp-decoder");
const log_1 = require("../utils/log");
const log = log_1.createLogger('jpeg-decoder');
const EXIF_HEADER = 0x45786966; // "Exif"

@@ -155,2 +157,12 @@ const XMP_HEADER = 0x68747470; // The "http" in "http://ns.adobe.com/xap/1.0/"

static isJPEG(buffer) {
try {
new JPEGDecoder(buffer)._readFileMarkers();
return true;
}
catch (err) {
log(`not a JPEG, decoding failed with ${err.message}`);
return false;
}
}
static isLikelyJPEG(buffer) {
return buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff;

@@ -157,0 +169,0 @@ }

@@ -15,2 +15,3 @@ /// <reference types="node" />

private _readStripOffsetsAsJPEG;
private _findJPEGInRange;
/**

@@ -22,2 +23,6 @@ * Panasonic mislabels all of their tags so the names make no sense.

private _readPanasonicJPEG;
/**
* Olympus JPEG preview is usually contained within the makernote
*/
private _readOlympusJPEG;
private _readLargestJPEG;

@@ -24,0 +29,0 @@ extractJPEG(options?: IJPEGOptions): IBufferLike;

@@ -34,2 +34,3 @@ "use strict";

this._reader = new reader_1.Reader(buffer);
this._variant = Variant.Unknown;
}

@@ -60,2 +61,3 @@ _readAndValidateHeader() {

}
log(`detected tiff variant as ${this._variant}`);
}

@@ -123,3 +125,3 @@ _readIFDs() {

});
if (!jpeg_decoder_1.JPEGDecoder.isJPEG(jpegBuffer))
if (!jpeg_decoder_1.JPEGDecoder.isLikelyJPEG(jpegBuffer))
return;

@@ -140,2 +142,45 @@ const jpeg = new jpeg_decoder_1.JPEGDecoder(jpegBuffer);

}
_findJPEGInRange(buffer, searchStartIndex, searchEndIndex) {
const startCandidates = [];
const endCandidates = [];
for (let i = searchStartIndex; i < searchEndIndex; i++) {
if (buffer[i] !== 0xff)
continue;
if (buffer[i + 1] !== 0xd8)
continue;
if (buffer[i + 2] !== 0xff)
continue;
startCandidates.push(i);
}
for (let i = searchEndIndex; i > searchStartIndex; i--) {
if (buffer[i - 1] !== 0xff)
continue;
if (buffer[i] !== 0xd9)
continue;
endCandidates.push(i);
}
let jpeg;
let maxWidth = -Infinity;
for (const startIndex of startCandidates) {
for (const endIndex of endCandidates) {
if (!Number.isFinite(startIndex))
continue;
if (!Number.isFinite(endIndex))
continue;
if (endIndex - startIndex < 4000)
continue;
const jpegBuffer = buffer.slice(startIndex, endIndex + 1);
if (!jpeg_decoder_1.JPEGDecoder.isJPEG(jpegBuffer))
continue;
const metadata = new jpeg_decoder_1.JPEGDecoder(jpegBuffer).extractMetadata();
if (typeof metadata.ImageWidth !== 'number')
continue;
if (metadata.ImageWidth < maxWidth)
continue;
maxWidth = metadata.ImageWidth;
jpeg = jpegBuffer;
}
}
return jpeg;
}
/**

@@ -157,33 +202,18 @@ * Panasonic mislabels all of their tags so the names make no sense.

return;
let startIndex = Infinity;
let endIndex = Infinity;
const buffer = this._reader.getBuffer();
for (let i = searchStart; i < endEntryValue; i++) {
if (buffer[i] !== 0xff)
continue;
if (buffer[i + 1] !== 0xd8)
continue;
if (buffer[i + 2] !== 0xff)
continue;
startIndex = i;
break;
}
for (let i = endEntryValue; i > searchStart; i--) {
if (buffer[i - 1] !== 0xff)
continue;
if (buffer[i] !== 0xd9)
continue;
endIndex = i;
break;
}
if (!Number.isFinite(startIndex))
return this._findJPEGInRange(this._reader.getBuffer(), searchStart, endEntryValue);
}
/**
* Olympus JPEG preview is usually contained within the makernote
*/
_readOlympusJPEG() {
if (this._variant !== Variant.Olympus)
return;
if (!Number.isFinite(endIndex))
const ifdEntries = this.extractIFDEntries();
const makerNoteEntry = ifdEntries.find(entry => entry.tag === 37500);
if (!makerNoteEntry)
return;
if (endIndex - startIndex < 4000)
const makernoteBuffer = makerNoteEntry.getValue(this._reader);
if (typeof makernoteBuffer === 'string' || typeof makernoteBuffer === 'number')
return;
const jpegBuffer = buffer.slice(startIndex, endIndex + 1);
if (!jpeg_decoder_1.JPEGDecoder.isJPEG(jpegBuffer))
return;
return jpegBuffer;
return this._findJPEGInRange(makernoteBuffer, 0, makernoteBuffer.length);
}

@@ -209,2 +239,5 @@ _readLargestJPEG() {

return panasonicJPEG;
const olympusJPEG = this._readOlympusJPEG();
if (olympusJPEG)
return olympusJPEG;
// Fail loudly if all else fails

@@ -237,3 +270,8 @@ throw new Error('Could not find an embedded JPEG');

const value = entry.getValue(this._reader);
log.verbose(`evaluated ${name} (${entry.tag} - ${entry.dataType}) as ${value}`);
const displayValue = typeof value === 'string' || typeof value === 'number'
? value.toString()
: value.slice(0, 32);
log.verbose(`evaluated ${name} (${entry.tag} - ${entry.dataType}) as ${displayValue}`);
if (typeof value !== 'string' && typeof value !== 'number')
return;
target[name] = value;

@@ -240,0 +278,0 @@ const panasonicName = tags_1.panasonicConversionTags[name];

@@ -41,3 +41,3 @@ "use strict";

}
else if (jpeg_decoder_1.JPEGDecoder.isJPEG(bufferOrDecoder)) {
else if (jpeg_decoder_1.JPEGDecoder.isLikelyJPEG(bufferOrDecoder)) {
return new jpeg_decoder_1.JPEGDecoder(bufferOrDecoder);

@@ -44,0 +44,0 @@ }

@@ -54,3 +54,3 @@ /// <reference types="node" />

length: number;
getValue(reader?: IReader): string | number;
getValue(reader?: IReader): string | number | IBufferLike;
}

@@ -57,0 +57,0 @@ export interface IIFDOffset {

@@ -51,2 +51,3 @@ "use strict";

case IFDDataType.Unknown: // ???
case IFDDataType.Undefined: // ???, was previously 4 but definitely not true of Olympus
case IFDDataType.Byte: // byte

@@ -62,3 +63,2 @@ case IFDDataType.String: // ASCII-string

case IFDDataType.Float:
case IFDDataType.Undefined:
return 4;

@@ -65,0 +65,0 @@ case IFDDataType.Rational: // rational number

import {getFriendlyName} from '../utils/tags'
import {IFDDataType, IIFDEntry, IReader, IFDTagName, getDataTypeSize, Endian} from '../utils/types'
import {
IFDDataType,
IIFDEntry,
IReader,
IFDTagName,
getDataTypeSize,
Endian,
IBufferLike,
} from '../utils/types'
import {createLogger} from '../utils/log'

@@ -26,3 +34,3 @@ import {Writer} from '../utils/writer'

public getValue(reader?: IReader): string | number {
public getValue(reader?: IReader): string | number | IBufferLike {
const entryReader = this.getReader(reader)

@@ -59,3 +67,3 @@ switch (this.dataType) {

case IFDDataType.Unknown:
return ''
return entryReader.getBuffer()
default:

@@ -62,0 +70,0 @@ throw new TypeError(`Unsupported data type (${this.dataType}) for tag (${this.tag})`)

@@ -6,3 +6,6 @@ import {TIFFDecoder} from '../decoder/tiff-decoder'

import {XMPDecoder} from './xmp-decoder'
import {createLogger} from '../utils/log'
const log = createLogger('jpeg-decoder')
const EXIF_HEADER = 0x45786966 // "Exif"

@@ -200,2 +203,12 @@ const XMP_HEADER = 0x68747470 // The "http" in "http://ns.adobe.com/xap/1.0/"

public static isJPEG(buffer: IBufferLike): boolean {
try {
new JPEGDecoder(buffer)._readFileMarkers()
return true
} catch (err) {
log(`not a JPEG, decoding failed with ${err.message}`)
return false
}
}
public static isLikelyJPEG(buffer: IBufferLike): boolean {
return buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff

@@ -202,0 +215,0 @@ }

@@ -61,2 +61,3 @@ import {IFD} from '../decoder/ifd'

this._reader = new Reader(buffer)
this._variant = Variant.Unknown
}

@@ -84,2 +85,4 @@

}
log(`detected tiff variant as ${this._variant}`)
}

@@ -159,3 +162,3 @@

if (!JPEGDecoder.isJPEG(jpegBuffer)) return
if (!JPEGDecoder.isLikelyJPEG(jpegBuffer)) return

@@ -179,2 +182,46 @@ const jpeg = new JPEGDecoder(jpegBuffer)

private _findJPEGInRange(
buffer: IBufferLike,
searchStartIndex: number,
searchEndIndex: number,
): IBufferLike | undefined {
const startCandidates: number[] = []
const endCandidates: number[] = []
for (let i = searchStartIndex; i < searchEndIndex; i++) {
if (buffer[i] !== 0xff) continue
if (buffer[i + 1] !== 0xd8) continue
if (buffer[i + 2] !== 0xff) continue
startCandidates.push(i)
}
for (let i = searchEndIndex; i > searchStartIndex; i--) {
if (buffer[i - 1] !== 0xff) continue
if (buffer[i] !== 0xd9) continue
endCandidates.push(i)
}
let jpeg: IBufferLike | undefined
let maxWidth = -Infinity
for (const startIndex of startCandidates) {
for (const endIndex of endCandidates) {
if (!Number.isFinite(startIndex)) continue
if (!Number.isFinite(endIndex)) continue
if (endIndex - startIndex < 4000) continue
const jpegBuffer = buffer.slice(startIndex, endIndex + 1)
if (!JPEGDecoder.isJPEG(jpegBuffer)) continue
const metadata = new JPEGDecoder(jpegBuffer).extractMetadata()
if (typeof metadata.ImageWidth !== 'number') continue
if (metadata.ImageWidth < maxWidth) continue
maxWidth = metadata.ImageWidth
jpeg = jpegBuffer
}
}
return jpeg
}
/**

@@ -195,28 +242,18 @@ * Panasonic mislabels all of their tags so the names make no sense.

let startIndex = Infinity
let endIndex = Infinity
const buffer = this._reader.getBuffer()
return this._findJPEGInRange(this._reader.getBuffer(), searchStart, endEntryValue)
}
for (let i = searchStart; i < endEntryValue; i++) {
if (buffer[i] !== 0xff) continue
if (buffer[i + 1] !== 0xd8) continue
if (buffer[i + 2] !== 0xff) continue
startIndex = i
break
}
/**
* Olympus JPEG preview is usually contained within the makernote
*/
private _readOlympusJPEG(): IBufferLike | undefined {
if (this._variant !== Variant.Olympus) return
for (let i = endEntryValue; i > searchStart; i--) {
if (buffer[i - 1] !== 0xff) continue
if (buffer[i] !== 0xd9) continue
endIndex = i
break
}
const ifdEntries = this.extractIFDEntries()
const makerNoteEntry = ifdEntries.find(entry => entry.tag === 37500)
if (!makerNoteEntry) return
if (!Number.isFinite(startIndex)) return
if (!Number.isFinite(endIndex)) return
if (endIndex - startIndex < 4000) return
const jpegBuffer = buffer.slice(startIndex, endIndex + 1)
if (!JPEGDecoder.isJPEG(jpegBuffer)) return
return jpegBuffer
const makernoteBuffer = makerNoteEntry.getValue(this._reader)
if (typeof makernoteBuffer === 'string' || typeof makernoteBuffer === 'number') return
return this._findJPEGInRange(makernoteBuffer, 0, makernoteBuffer.length)
}

@@ -241,2 +278,4 @@

if (panasonicJPEG) return panasonicJPEG
const olympusJPEG = this._readOlympusJPEG()
if (olympusJPEG) return olympusJPEG

@@ -274,3 +313,8 @@ // Fail loudly if all else fails

const value = entry.getValue(this._reader)
log.verbose(`evaluated ${name} (${entry.tag} - ${entry.dataType}) as ${value}`)
const displayValue =
typeof value === 'string' || typeof value === 'number'
? value.toString()
: value.slice(0, 32)
log.verbose(`evaluated ${name} (${entry.tag} - ${entry.dataType}) as ${displayValue}`)
if (typeof value !== 'string' && typeof value !== 'number') return
target[name] = value

@@ -277,0 +321,0 @@

@@ -32,3 +32,3 @@ import {JPEGDecoder} from './decoder/jpeg-decoder'

return new TIFFDecoder(bufferOrDecoder)
} else if (JPEGDecoder.isJPEG(bufferOrDecoder)) {
} else if (JPEGDecoder.isLikelyJPEG(bufferOrDecoder)) {
return new JPEGDecoder(bufferOrDecoder)

@@ -35,0 +35,0 @@ } else if (XMPDecoder.isXMP(bufferOrDecoder)) {

@@ -62,3 +62,3 @@ export interface ILogger {

length: number
getValue(reader?: IReader): string | number
getValue(reader?: IReader): string | number | IBufferLike
}

@@ -166,2 +166,3 @@

case IFDDataType.Unknown: // ???
case IFDDataType.Undefined: // ???, was previously 4 but definitely not true of Olympus
case IFDDataType.Byte: // byte

@@ -177,3 +178,2 @@ case IFDDataType.String: // ASCII-string

case IFDDataType.Float:
case IFDDataType.Undefined:
return 4

@@ -180,0 +180,0 @@ case IFDDataType.Rational: // rational number

{
"name": "@eris/exif",
"version": "0.4.2-alpha.14",
"version": "0.4.2-alpha.15",
"description": "Parses EXIF data.",

@@ -41,3 +41,3 @@ "main": "./dist/index.js",

},
"gitHead": "7ef5d393ef281749cb3ba173abf70056f879dad5"
"gitHead": "61be137dec5c0d3a2dafd1065611f3091dbbd60c"
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc