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.3.1-alpha.0 to 0.3.1-alpha.1

dist/metadata/keywords-parser.d.ts

1

dist/decoder/xmp-decoder.d.ts

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

private _handleMatch;
private _decodeKeywords;
extractMetadata(): IGenericMetadata;
static isXMP(buffer: IBufferLike): boolean;
}

@@ -12,2 +12,18 @@ "use strict";

}
function getXMLTagRegExp(tag, flags) {
return new RegExp(`<${tag}>((.|\\s)*?)</${tag}>`, flags);
}
function findXMLTag(text, tag) {
const regex = getXMLTagRegExp(tag, 'i');
const match = text.match(regex);
if (!match)
return null;
return { innerXML: match[1] };
}
function findXMLTags(text, tag) {
const matches = text.match(getXMLTagRegExp(tag, 'ig'));
if (!matches)
return [];
return matches.map(item => findXMLTag(item, tag));
}
class XMPDecoder {

@@ -35,2 +51,14 @@ constructor(buffer) {

}
_decodeKeywords(genericMetadata) {
const subjectEl = findXMLTag(this._text, 'dc:subject');
if (!subjectEl)
return;
const bagEl = findXMLTag(subjectEl.innerXML, 'rdf:Bag');
if (!bagEl)
return;
const keywords = findXMLTags(bagEl.innerXML, 'rdf:li');
if (!keywords.length)
return;
genericMetadata.DCSubjectBagOfWords = JSON.stringify(keywords.map(item => item.innerXML));
}
extractMetadata() {

@@ -44,2 +72,3 @@ const metadata = {};

}
this._decodeKeywords(metadata);
return metadata;

@@ -46,0 +75,0 @@ }

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

private static _findExistingTag;
private static _handleKeywords;
private static _generateKeywordsPayload;
private static _findIndexOfRdfDescriptionEnd;
}

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

const log_1 = require("../utils/log");
const keywords_parser_1 = require("../metadata/keywords-parser");
const log = log_1.createLogger('xmp-encoder');

@@ -26,2 +27,3 @@ const XMP_BASE_FILE = `

const XMP_PACKET_END = '<?xpacket end="w"?>';
const BASE_NEWLINE = '\n ';
class XMPEncoder {

@@ -55,7 +57,26 @@ static isWrappedInPacket(xmpData) {

log(`examining ${tagName}`);
if (typeof metadata[tagName] === 'undefined')
continue;
if (!(tagName in tags_1.xmpTags))
continue;
const value = metadata[tagName];
if (tagName === 'DCSubjectBagOfWords') {
const result = XMPEncoder._handleKeywords(xmpData, metadata);
xmpData = result.xmpData;
extraLength += result.extraLength;
continue;
}
if (typeof value === 'undefined') {
log(`unsetting ${tagName}`);
const existing = XMPEncoder._findExistingTag(xmpData, tagName);
if (!existing)
continue;
let preamble = xmpData.slice(0, existing.start);
const postamble = xmpData.slice(existing.start + existing.length);
if (postamble.startsWith('\n'))
preamble = preamble.replace(/\n +$/, '');
const beforeLength = xmpData.length;
xmpData = `${preamble}${postamble}`;
const afterLength = xmpData.length;
extraLength += afterLength - beforeLength;
continue;
}
log(`writing ${tagName} as "${value}"`);

@@ -65,2 +86,3 @@ const existing = XMPEncoder._findExistingTag(xmpData, tagName);

if (existing) {
log(`found existing ${tagName} - ${existing.value}`);
const preamble = xmpData.slice(0, existing.start);

@@ -73,6 +95,7 @@ const postamble = xmpData.slice(existing.start + existing.length);

else {
log(`did not find existing ${tagName}`);
const rdfEnd = XMPEncoder._findIndexOfRdfDescriptionEnd(xmpData);
const preamble = xmpData.slice(0, rdfEnd);
const postamble = xmpData.slice(rdfEnd);
const replacementWithNewline = `\n ${replacement}`;
const replacementWithNewline = `${BASE_NEWLINE}${replacement}`;
xmpData = `${preamble}${replacementWithNewline}${postamble}`;

@@ -89,2 +112,4 @@ extraLength += replacementWithNewline.length;

if (existingWhitespaceLength > extraLength) {
// We only need to adjust the whitespace if we had enough room to fit our data into it
log(`adjusting whitespace, our ${extraLength} will fit into ${existingWhitespaceLength}`);
const indexOfMatch = xmpData.indexOf(existingWhitespaceMatch[0]);

@@ -109,2 +134,50 @@ const preamble = xmpData.slice(0, indexOfMatch);

}
static _handleKeywords(xmpData, metadata) {
const newKeywords = keywords_parser_1.parseKeywords(metadata);
const existingKeywordsMatch = xmpData.match(/<dc:subject>(.|\s)*?<\/dc:subject>/);
if (!newKeywords || !newKeywords.length) {
// Nothing to remove, move on
if (!existingKeywordsMatch)
return { xmpData, extraLength: 0 };
// Remove the payload
const indexOfMatch = existingKeywordsMatch.index;
const original = existingKeywordsMatch[0];
const preamble = xmpData.slice(0, indexOfMatch);
const postamble = xmpData.slice(indexOfMatch + original.length);
return {
xmpData: `${preamble}${postamble}`,
extraLength: -original.length,
};
}
const replacement = XMPEncoder._generateKeywordsPayload(newKeywords);
if (existingKeywordsMatch) {
const indexOfMatch = existingKeywordsMatch.index;
const original = existingKeywordsMatch[0];
const preamble = xmpData.slice(0, indexOfMatch);
const postamble = xmpData.slice(indexOfMatch + original.length);
return {
xmpData: `${preamble}${replacement}${postamble}`,
extraLength: replacement.length - original.length,
};
}
else {
const rdfEnd = XMPEncoder._findIndexOfRdfDescriptionEnd(xmpData) + 1;
const preamble = xmpData.slice(0, rdfEnd);
const postamble = xmpData.slice(rdfEnd);
const replacementWithNewline = `${BASE_NEWLINE}${replacement}`;
return {
xmpData: `${preamble}${replacementWithNewline}${postamble}`,
extraLength: replacementWithNewline.length,
};
}
}
static _generateKeywordsPayload(keywords) {
return [
`<dc:subject>`,
` <rdf:Bag>`,
...keywords.map(word => ` <rdf:li>${word.replace(/</g, '')}</rdf:li>`),
` </rdf:Bag>`,
`</dc:subject>`,
].join(BASE_NEWLINE);
}
static _findIndexOfRdfDescriptionEnd(xmp) {

@@ -111,0 +184,0 @@ const regex = /<rdf:Description(\s*(\w+:\w+=".*?")\s*)*?>/im;

6

dist/metadata/normalize.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const date_parser_1 = require("../metadata/date-parser");
const lens_parser_1 = require("../metadata/lens-parser");
const date_parser_1 = require("./date-parser");
const keywords_parser_1 = require("./keywords-parser");
const lens_parser_1 = require("./lens-parser");
const properties = {

@@ -24,2 +25,3 @@ // TODO: look into how to normalize GPS coordinates

colorLabel: ['Label'],
keywords: [keywords_parser_1.parseKeywords],
};

@@ -26,0 +28,0 @@ function getResultValue(item, results) {

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

MetadataDate: true,
DCSubjectBagOfWords: true,
};

@@ -14,0 +15,0 @@ // TODO: fill in all IFDDataTypes with -1

@@ -120,5 +120,6 @@ /// <reference types="node" />

colorLabel?: 'Blue' | 'Red' | 'Purple' | 'Yellow' | 'Green';
keywords?: string[];
}
export declare function getDataTypeSize(dataType: number, name?: string | number): number;
export declare type XMPTagName = 'Rating' | 'Label' | 'MetadataDate';
export declare type XMPTagName = 'Rating' | 'Label' | 'MetadataDate' | 'DCSubjectBagOfWords';
export declare type IFDTagName = 'Unknown' | 'ImageWidth' | 'NewSubfileType' | 'SubfileType' | 'ImageWidth' | 'ImageLength' | 'BitsPerSample' | 'Compression' | 'PhotometricInterpretation' | 'Thresholding' | 'CellWidth' | 'CellLength' | 'FillOrder' | 'DocumentName' | 'ImageDescription' | 'Make' | 'Model' | 'StripOffsets' | 'Orientation' | 'SamplesPerPixel' | 'RowsPerStrip' | 'StripByteCounts' | 'MinSampleValue' | 'MaxSampleValue' | 'XResolution' | 'YResolution' | 'PlanarConfiguration' | 'PageName' | 'XPosition' | 'YPosition' | 'FreeOffsets' | 'FreeByteCounts' | 'GrayResponseUnit' | 'GrayResponseCurve' | 'T4Options' | 'T6Options' | 'ResolutionUnit' | 'PageNumber' | 'ColorResponseUnit' | 'TransferFunction' | 'Software' | 'ModifyDate' | 'Artist' | 'HostComputer' | 'Predictor' | 'WhitePoint' | 'PrimaryChromaticities' | 'ColorMap' | 'HalftoneHints' | 'TileWidth' | 'TileLength' | 'TileOffsets' | 'TileByteCounts' | 'BadFaxLines' | 'CleanFaxData' | 'ConsecutiveBadFaxLines' | 'SubIFD' | 'InkSet' | 'InkNames' | 'NumberOfInks' | 'DotRange' | 'TargetPrinter' | 'ExtraSamples' | 'SampleFormat' | 'SMinSampleValue' | 'SMaxSampleValue' | 'TransferRange' | 'ClipPath' | 'XClipPathUnits' | 'YClipPathUnits' | 'Indexed' | 'JPEGTables' | 'OPIProxy' | 'GlobalParametersIFD' | 'ProfileType' | 'FaxProfile' | 'CodingMethods' | 'VersionYear' | 'ModeNumber' | 'Decode' | 'DefaultImageColor' | 'T82Options' | 'JPEGProc' | 'JPEGInterchangeFormat' | 'JPEGInterchangeFormatLength' | 'JPEGRestartInterval' | 'JPEGLosslessPredictors' | 'JPEGPointTransforms' | 'JPEGQTables' | 'JPEGDCTables' | 'JPEGACTables' | 'YCbCrCoefficients' | 'YCbCrSubSampling' | 'YCbCrPositioning' | 'ReferenceBlackWhite' | 'StripRowCounts' | 'XMLPacket' | 'USPTOMiscellaneous' | 'RelatedImageFileFormat' | 'RelatedImageWidth' | 'RelatedImageHeight' | 'Rating' | 'XP_DIP_XML' | 'StitchInfo' | 'RatingPercent' | 'ImageID' | 'WangTag1' | 'WangAnnotation' | 'WangTag3' | 'WangTag4' | 'Matteing' | 'DataType' | 'ImageDepth' | 'TileDepth' | 'Model2' | 'CFARepeatPatternDim' | 'CFAPattern' | 'BatteryLevel' | 'KodakIFD' | 'Copyright' | 'ExposureTime' | 'FNumber' | 'MDFileTag' | 'MDScalePixel' | 'MDColorTable' | 'MDLabName' | 'MDSampleInfo' | 'MDPrepDate' | 'MDPrepTime' | 'MDFileUnits' | 'PixelScale' | 'AdventScale' | 'AdventRevision' | 'UIC1Tag' | 'UIC2Tag' | 'UIC3Tag' | 'UIC4Tag' | 'IPTC-NAA' | 'IntergraphPacketData' | 'IntergraphFlagRegisters' | 'IntergraphMatrix' | 'INGRReserved' | 'ModelTiePoint' | 'Site' | 'ColorSequence' | 'IT8Header' | 'RasterPadding' | 'BitsPerRunLength' | 'BitsPerExtendedRunLength' | 'ColorTable' | 'ImageColorIndicator' | 'BackgroundColorIndicator' | 'ImageColorValue' | 'BackgroundColorValue' | 'PixelIntensityRange' | 'TransparencyIndicator' | 'ColorCharacterization' | 'HCUsage' | 'TrapIndicator' | 'CMYKEquivalent' | 'SEMInfo' | 'AFCP_IPTC' | 'PixelMagicJBIGOptions' | 'ModelTransform' | 'WB_GRGBLevels' | 'LeafData' | 'PhotoshopSettings' | 'EXIFTag' | 'InterColorProfile' | 'TIFF_FXExtensions' | 'MultiProfiles' | 'SharedData' | 'T88Options' | 'ImageLayer' | 'GeoTiffDirectory' | 'GeoTiffDoubleParams' | 'GeoTiffAsciiParams' | 'ExposureProgram' | 'SpectralSensitivity' | 'GPSTag' | 'ISO' | 'Opto-ElectricConvFactor' | 'Interlace' | 'TimeZoneOffset' | 'SelfTimerMode' | 'SensitivityType' | 'StandardOutputSensitivity' | 'RecommendedExposureIndex' | 'ISOSpeed' | 'ISOSpeedLatitudeyyy' | 'ISOSpeedLatitudezzz' | 'FaxRecvParams' | 'FaxSubAddress' | 'FaxRecvTime' | 'LeafSubIFD' | 'EXIFVersion' | 'DateTimeOriginal' | 'CreateDate' | 'ComponentsConfiguration' | 'CompressedBitsPerPixel' | 'ShutterSpeedValue' | 'ApertureValue' | 'BrightnessValue' | 'ExposureCompensation' | 'MaxApertureValue' | 'SubjectDistance' | 'MeteringMode' | 'LightSource' | 'Flash' | 'FocalLength' | 'FlashEnergy' | 'SpatialFrequencyResponse' | 'Noise' | 'FocalPlaneXResolution' | 'FocalPlaneYResolution' | 'FocalPlaneResolutionUnit' | 'ImageNumber' | 'SecurityClassification' | 'ImageHistory' | 'SubjectArea' | 'ExposureIndex' | 'TIFFEPStandardID' | 'SensingMethod' | 'CIP3DataFile' | 'CIP3Sheet' | 'CIP3Side' | 'StoNits' | 'MakerNote' | 'UserComment' | 'SubSecTime' | 'SubSecTimeOriginal' | 'SubSecTimeDigitized' | 'MSDocumentText' | 'MSPropertySetStorage' | 'MSDocumentTextPosition' | 'ImageSourceData' | 'XPTitle' | 'XPComment' | 'XPAuthor' | 'XPKeywords' | 'XPSubject' | 'FlashpixVersion' | 'ColorSpace' | 'EXIFImageWidth' | 'EXIFImageHeight' | 'RelatedSoundFile' | 'InteropOffset' | 'SubjectLocation' | 'TIFF-EPStandardID' | 'FileSource' | 'SceneType' | 'CustomRendered' | 'ExposureMode' | 'WhiteBalance' | 'DigitalZoomRatio' | 'FocalLengthIn35mmFormat' | 'SceneCaptureType' | 'GainControl' | 'Contrast' | 'Saturation' | 'Sharpness' | 'DeviceSettingDescription' | 'SubjectDistanceRange' | 'ImageUniqueID' | 'OwnerName' | 'SerialNumber' | 'LensMake' | 'LensModel' | 'LensSerialNumber' | 'GDALMetadata' | 'GDALNoData' | 'Gamma' | 'ExpandSoftware' | 'ExpandLens' | 'ExpandFilm' | 'ExpandFilterLens' | 'ExpandScanner' | 'ExpandFlashLamp' | 'PixelFormat' | 'Transformation' | 'Uncompressed' | 'ImageType' | 'WidthResolution' | 'HeightResolution' | 'ImageOffset' | 'ImageByteCount' | 'AlphaOffset' | 'AlphaByteCount' | 'ImageDataDiscard' | 'AlphaDataDiscard' | 'OceScanjobDesc' | 'OceApplicationSelector' | 'OceIDNumber' | 'OceImageLogic' | 'Annotations' | 'PrintImageMatching' | 'USPTOOriginalContentType' | 'DNGVersion' | 'DNGBackwardVersion' | 'UniqueCameraModel' | 'LocalizedCameraModel' | 'CFAPlaneColor' | 'CFALayout' | 'LinearizationTable' | 'BlackLevelRepeatDim' | 'BlackLevel' | 'BlackLevelDeltaH' | 'BlackLevelDeltaV' | 'WhiteLevel' | 'DefaultScale' | 'DefaultCropOrigin' | 'DefaultCropSize' | 'ColorMatrix1' | 'ColorMatrix2' | 'CameraCalibration1' | 'CameraCalibration2' | 'ReductionMatrix1' | 'ReductionMatrix2' | 'AnalogBalance' | 'AsShotNeutral' | 'AsShotWhiteXY' | 'BaselineExposure' | 'BaselineNoise' | 'BaselineSharpness' | 'BayerGreenSplit' | 'LinearResponseLimit' | 'CameraSerialNumber' | 'LensInfo' | 'ChromaBlurRadius' | 'AntiAliasStrength' | 'ShadowScale' | 'DNGPrivateData' | 'MakerNoteSafety' | 'RawImageSegmentation' | 'CalibrationIlluminant1' | 'CalibrationIlluminant2' | 'BestQualityScale' | 'RawDataUniqueID' | 'AliasLayerMetadata' | 'OriginalRawFileName' | 'OriginalRawFileData' | 'ActiveArea' | 'MaskedAreas' | 'AsShotICCProfile' | 'AsShotPreProfileMatrix' | 'CurrentICCProfile' | 'CurrentPreProfileMatrix' | 'ColorimetricReference' | 'PanasonicTitle' | 'PanasonicTitle2' | 'CameraCalibrationSignature' | 'ProfileCalibrationSignature' | 'ProfileIFD' | 'AsShotProfileName' | 'NoiseReductionApplied' | 'ProfileName' | 'ProfileHueSatMapDims' | 'ProfileHueSatMapData1' | 'ProfileHueSatMapData2' | 'ProfileToneCurve' | 'ProfileEmbedPolicy' | 'ProfileCopyright' | 'ForwardMatrix1' | 'ForwardMatrix2' | 'PreviewApplicationName' | 'PreviewApplicationVersion' | 'PreviewSettingsName' | 'PreviewSettingsDigest' | 'PreviewColorSpace' | 'PreviewDateTime' | 'RawImageDigest' | 'OriginalRawFileDigest' | 'SubTileBlockSize' | 'RowInterleaveFactor' | 'ProfileLookTableDims' | 'ProfileLookTableData' | 'OpcodeList1' | 'OpcodeList2' | 'OpcodeList3' | 'NoiseProfile' | 'TimeCodes' | 'FrameRate' | 'TStop' | 'ReelName' | 'OriginalDefaultFinalSize' | 'OriginalBestQualitySize' | 'OriginalDefaultCropSize' | 'CameraLabel' | 'ProfileHueSatMapEncoding' | 'ProfileLookTableEncoding' | 'BaselineExposureOffset' | 'DefaultBlackRender' | 'NewRawImageDigest' | 'RawToPreviewGain' | 'DefaultUserCrop' | 'Padding' | 'OffsetSchema' | 'OwnerName' | 'SerialNumber' | 'Lens' | 'KDC_IFD' | 'RawFile' | 'Converter' | 'WhiteBalance' | 'Exposure' | 'Shadows' | 'Brightness' | 'Contrast' | 'Saturation' | 'Sharpness' | 'Smoothness' | 'MoireFilter' | 'GPSVersionID' | 'GPSLatitudeRef' | 'GPSLatitude' | 'GPSLongitudeRef' | 'GPSLongitude' | 'GPSAltitudeRef' | 'GPSAltitude' | 'GPSTimeStamp' | 'GPSSatellites' | 'GPSStatus' | 'GPSMeasureMode' | 'GPSDOP' | 'GPSSpeedRef' | 'GPSSpeed' | 'GPSTrackRef' | 'GPSTrack' | 'GPSImgDirectionRef' | 'GPSImgDirection' | 'GPSMapDatum' | 'GPSDestLatitudeRef' | 'GPSDestLatitude' | 'GPSDestLongitudeRef' | 'GPSDestLongitude' | 'GPSDestBearingRef' | 'GPSDestBearing' | 'GPSDestDistanceRef' | 'GPSDestDistance' | 'GPSProcessingMethod' | 'GPSAreaInformation' | 'GPSDateStamp' | 'GPSDifferential' | 'GPSHPositioningError';

@@ -15,2 +15,19 @@ import {IBufferLike, IGenericMetadata, IFDTagName, XMPTagName} from '../utils/types'

function getXMLTagRegExp(tag: string, flags?: string): RegExp {
return new RegExp(`<${tag}>((.|\\s)*?)</${tag}>`, flags)
}
function findXMLTag(text: string, tag: string): {innerXML: string} | null {
const regex = getXMLTagRegExp(tag, 'i')
const match = text.match(regex)
if (!match) return null
return {innerXML: match[1]}
}
function findXMLTags(text: string, tag: string): Array<{innerXML: string}> {
const matches = text.match(getXMLTagRegExp(tag, 'ig'))
if (!matches) return []
return matches.map(item => findXMLTag(item, tag)!)
}
export class XMPDecoder {

@@ -41,2 +58,12 @@ private readonly _text: string

private _decodeKeywords(genericMetadata: IGenericMetadata): void {
const subjectEl = findXMLTag(this._text, 'dc:subject')
if (!subjectEl) return
const bagEl = findXMLTag(subjectEl.innerXML, 'rdf:Bag')
if (!bagEl) return
const keywords = findXMLTags(bagEl.innerXML, 'rdf:li')
if (!keywords.length) return
genericMetadata.DCSubjectBagOfWords = JSON.stringify(keywords.map(item => item.innerXML))
}
public extractMetadata(): IGenericMetadata {

@@ -52,2 +79,3 @@ const metadata: IGenericMetadata = {}

this._decodeKeywords(metadata)
return metadata

@@ -54,0 +82,0 @@ }

import {IGenericMetadata, IBufferLike} from '../utils/types'
import {xmpTags} from '../utils/tags'
import {createLogger} from '../utils/log'
import {parseKeywords} from '../metadata/keywords-parser'

@@ -27,2 +28,3 @@ const log = createLogger('xmp-encoder')

const XMP_PACKET_END = '<?xpacket end="w"?>'
const BASE_NEWLINE = '\n '

@@ -72,6 +74,27 @@ interface TagLocation {

log(`examining ${tagName}`)
if (typeof metadata[tagName] === 'undefined') continue
if (!(tagName in xmpTags)) continue
const value = metadata[tagName]
if (tagName === 'DCSubjectBagOfWords') {
const result = XMPEncoder._handleKeywords(xmpData, metadata)
xmpData = result.xmpData
extraLength += result.extraLength
continue
}
if (typeof value === 'undefined') {
log(`unsetting ${tagName}`)
const existing = XMPEncoder._findExistingTag(xmpData, tagName)
if (!existing) continue
let preamble = xmpData.slice(0, existing.start)
const postamble = xmpData.slice(existing.start + existing.length)
if (postamble.startsWith('\n')) preamble = preamble.replace(/\n +$/, '')
const beforeLength = xmpData.length
xmpData = `${preamble}${postamble}`
const afterLength = xmpData.length
extraLength += afterLength - beforeLength
continue
}
log(`writing ${tagName} as "${value}"`)

@@ -83,2 +106,3 @@

if (existing) {
log(`found existing ${tagName} - ${existing.value}`)
const preamble = xmpData.slice(0, existing.start)

@@ -90,6 +114,7 @@ const postamble = xmpData.slice(existing.start + existing.length)

} else {
log(`did not find existing ${tagName}`)
const rdfEnd = XMPEncoder._findIndexOfRdfDescriptionEnd(xmpData)
const preamble = xmpData.slice(0, rdfEnd)
const postamble = xmpData.slice(rdfEnd)
const replacementWithNewline = `\n ${replacement}`
const replacementWithNewline = `${BASE_NEWLINE}${replacement}`
xmpData = `${preamble}${replacementWithNewline}${postamble}`

@@ -104,4 +129,7 @@ extraLength += replacementWithNewline.length

if (!existingWhitespaceMatch) throw new Error('Cannot find XMP packet end')
const existingWhitespaceLength = existingWhitespaceMatch[1].length
if (existingWhitespaceLength > extraLength) {
// We only need to adjust the whitespace if we had enough room to fit our data into it
log(`adjusting whitespace, our ${extraLength} will fit into ${existingWhitespaceLength}`)
const indexOfMatch = xmpData.indexOf(existingWhitespaceMatch[0])

@@ -128,2 +156,55 @@ const preamble = xmpData.slice(0, indexOfMatch)

private static _handleKeywords(
xmpData: string,
metadata: IGenericMetadata,
): {xmpData: string; extraLength: number} {
const newKeywords = parseKeywords(metadata)
const existingKeywordsMatch = xmpData.match(/<dc:subject>(.|\s)*?<\/dc:subject>/)
if (!newKeywords || !newKeywords.length) {
// Nothing to remove, move on
if (!existingKeywordsMatch) return {xmpData, extraLength: 0}
// Remove the payload
const indexOfMatch = existingKeywordsMatch.index!
const original = existingKeywordsMatch[0]
const preamble = xmpData.slice(0, indexOfMatch)
const postamble = xmpData.slice(indexOfMatch + original.length)
return {
xmpData: `${preamble}${postamble}`,
extraLength: -original.length,
}
}
const replacement = XMPEncoder._generateKeywordsPayload(newKeywords)
if (existingKeywordsMatch) {
const indexOfMatch = existingKeywordsMatch.index!
const original = existingKeywordsMatch[0]
const preamble = xmpData.slice(0, indexOfMatch)
const postamble = xmpData.slice(indexOfMatch + original.length)
return {
xmpData: `${preamble}${replacement}${postamble}`,
extraLength: replacement.length - original.length,
}
} else {
const rdfEnd = XMPEncoder._findIndexOfRdfDescriptionEnd(xmpData) + 1
const preamble = xmpData.slice(0, rdfEnd)
const postamble = xmpData.slice(rdfEnd)
const replacementWithNewline = `${BASE_NEWLINE}${replacement}`
return {
xmpData: `${preamble}${replacementWithNewline}${postamble}`,
extraLength: replacementWithNewline.length,
}
}
}
private static _generateKeywordsPayload(keywords: string[]): string {
return [
`<dc:subject>`,
` <rdf:Bag>`,
...keywords.map(word => ` <rdf:li>${word.replace(/</g, '')}</rdf:li>`),
` </rdf:Bag>`,
`</dc:subject>`,
].join(BASE_NEWLINE)
}
private static _findIndexOfRdfDescriptionEnd(xmp: string): number {

@@ -130,0 +211,0 @@ const regex = /<rdf:Description(\s*(\w+:\w+=".*?")\s*)*?>/im

@@ -1,3 +0,4 @@

import {parseDate} from '../metadata/date-parser'
import {parseLens} from '../metadata/lens-parser'
import {parseDate} from './date-parser'
import {parseKeywords} from './keywords-parser'
import {parseLens} from './lens-parser'
import {INormalizedMetadata, IGenericMetadata, IFDTagName, XMPTagName} from '../utils/types'

@@ -40,2 +41,3 @@

colorLabel: ['Label'],
keywords: [parseKeywords],
}

@@ -42,0 +44,0 @@

@@ -14,2 +14,3 @@ import {IIFDTagDefinition, IFDTagName, IFDGroup, IFDDataType, XMPTagName} from './types'

MetadataDate: true,
DCSubjectBagOfWords: true,
}

@@ -16,0 +17,0 @@

@@ -146,2 +146,3 @@ export interface ILogger {

colorLabel?: 'Blue' | 'Red' | 'Purple' | 'Yellow' | 'Green'
keywords?: string[]
}

@@ -172,3 +173,3 @@

export type XMPTagName = 'Rating' | 'Label' | 'MetadataDate'
export type XMPTagName = 'Rating' | 'Label' | 'MetadataDate' | 'DCSubjectBagOfWords'

@@ -175,0 +176,0 @@ export type IFDTagName =

{
"name": "@eris/exif",
"version": "0.3.1-alpha.0",
"version": "0.3.1-alpha.1",
"description": "Parses EXIF data.",

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

},
"gitHead": "56e73cf75e7c5a72ac2053fb532041a77e3d12bd"
"gitHead": "3ecabcb8242310727ef6c247feae54977438f9d2"
}

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