@eris/exif
Advanced tools
Comparing version 0.3.1-alpha.1 to 0.3.1-alpha.2
@@ -7,6 +7,6 @@ import { IGenericMetadata, IBufferLike } from '../utils/types'; | ||
static encode(metadata: IGenericMetadata, original?: IBufferLike): IBufferLike; | ||
private static _findExistingTag; | ||
private static _handleKeywords; | ||
private static _generateKeywordsPayload; | ||
private static _findIndexOfRdfDescriptionEnd; | ||
private static _processEntry; | ||
private static _findExisting; | ||
private static _findInsertionPoint; | ||
private static _buildReplacement; | ||
} |
@@ -54,47 +54,9 @@ "use strict"; | ||
const tagName = key; | ||
log(`examining ${tagName}`); | ||
if (!(tagName in tags_1.xmpTags)) | ||
if (!(tagName in tags_1.xmpTags)) { | ||
log(`skipping ${tagName} which is not an XMP tag`); | ||
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}"`); | ||
const existing = XMPEncoder._findExistingTag(xmpData, tagName); | ||
const replacement = `xmp:${tagName}="${value}"`; | ||
if (existing) { | ||
log(`found existing ${tagName} - ${existing.value}`); | ||
const preamble = xmpData.slice(0, existing.start); | ||
const postamble = xmpData.slice(existing.start + existing.length); | ||
const additionalLength = replacement.length - existing.length; | ||
xmpData = `${preamble}${replacement}${postamble}`; | ||
extraLength += additionalLength; | ||
} | ||
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 = `${BASE_NEWLINE}${replacement}`; | ||
xmpData = `${preamble}${replacementWithNewline}${postamble}`; | ||
extraLength += replacementWithNewline.length; | ||
} | ||
const newXmpData = XMPEncoder._processEntry(xmpData, tagName, metadata[tagName]); | ||
extraLength += newXmpData.length - xmpData.length; | ||
xmpData = newXmpData; | ||
} | ||
@@ -120,51 +82,64 @@ if (XMPEncoder.isWrappedInPacket(xmpData)) { | ||
} | ||
static _findExistingTag(xmp, tagName) { | ||
const regex = new RegExp(`([a-z]+):(${tagName})="(.*?)"`, 'i'); | ||
const match = xmp.match(regex); | ||
if (!match) | ||
return undefined; | ||
const [fullMatch, prefix, name, value] = match; | ||
const start = xmp.indexOf(fullMatch); | ||
return { start, length: fullMatch.length, prefix, name, value }; | ||
} | ||
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, | ||
}; | ||
static _processEntry(xmpData, tagName, value) { | ||
log(`processing ${tagName}`); | ||
const existing = XMPEncoder._findExisting(xmpData, tagName); | ||
// If we are unsetting, branch. | ||
if (typeof value === 'undefined') { | ||
if (!existing) { | ||
// If we didn't have an existing value to begin with, we're done. | ||
log(`${tagName} already missing from XMP, skipping`); | ||
return xmpData; | ||
} | ||
log(`unsetting ${tagName}`); | ||
// Remove the existing reference and cleanup whitespace | ||
let preamble = xmpData.slice(0, existing.start); | ||
const postamble = xmpData.slice(existing.start + existing.length); | ||
if (postamble.match(/^(\n|>)/)) | ||
preamble = preamble.replace(/\n +$/, ''); | ||
return `${preamble}${postamble}`; | ||
} | ||
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, | ||
}; | ||
log(`writing ${tagName} with value "${value}"`); | ||
const replacement = XMPEncoder._buildReplacement(tagName, value); | ||
if (existing) { | ||
// If we have an existing value, replace the token range with our new payload | ||
log(`found existing ${tagName}`); | ||
const preamble = xmpData.slice(0, existing.start); | ||
const postamble = xmpData.slice(existing.start + existing.length); | ||
return `${preamble}${replacement}${postamble}`; | ||
} | ||
else { | ||
const rdfEnd = XMPEncoder._findIndexOfRdfDescriptionEnd(xmpData) + 1; | ||
const preamble = xmpData.slice(0, rdfEnd); | ||
const postamble = xmpData.slice(rdfEnd); | ||
// If we don't have an existing value, inject the payload with appropriate whitespace. | ||
log(`did not find existing ${tagName}`); | ||
const insertionIndex = XMPEncoder._findInsertionPoint(xmpData, tagName).start; | ||
const preamble = xmpData.slice(0, insertionIndex); | ||
const postamble = xmpData.slice(insertionIndex); | ||
const replacementWithNewline = `${BASE_NEWLINE}${replacement}`; | ||
return { | ||
xmpData: `${preamble}${replacementWithNewline}${postamble}`, | ||
extraLength: replacementWithNewline.length, | ||
}; | ||
return `${preamble}${replacementWithNewline}${postamble}`; | ||
} | ||
} | ||
static _generateKeywordsPayload(keywords) { | ||
static _findExisting(xmp, tagName) { | ||
const regex = tagName === 'DCSubjectBagOfWords' | ||
? /<dc:subject>(.|\s)*?<\/dc:subject>/ | ||
: new RegExp(`([a-z]+):(${tagName})="(.*?)"`, 'i'); | ||
const match = xmp.match(regex); | ||
if (!match) | ||
return; | ||
return { start: match.index, length: match[0].length }; | ||
} | ||
static _findInsertionPoint(xmp, tagName) { | ||
const regex = /<rdf:Description[^<]*?>/im; | ||
const match = xmp.match(regex); | ||
if (!match) | ||
throw new Error('Unable to find end of rdf:description'); | ||
const rdfDescription = match[0]; | ||
const rdfDescriptionEndIndex = xmp.indexOf(rdfDescription) + rdfDescription.length - 1; | ||
const start = rdfDescriptionEndIndex + (tagName === 'DCSubjectBagOfWords' ? 1 : 0); | ||
return { start }; | ||
} | ||
static _buildReplacement(tagName, value) { | ||
if (tagName !== 'DCSubjectBagOfWords') | ||
return `xmp:${tagName}="${value}"`; | ||
const keywords = keywords_parser_1.parseKeywords({ DCSubjectBagOfWords: value }); | ||
if (!keywords) | ||
throw new Error('Invalid keywords payload'); | ||
return [ | ||
@@ -178,12 +153,4 @@ `<dc:subject>`, | ||
} | ||
static _findIndexOfRdfDescriptionEnd(xmp) { | ||
const regex = /<rdf:Description(\s*(\w+:\w+=".*?")\s*)*?>/im; | ||
const match = xmp.match(regex); | ||
if (!match) | ||
throw new Error('Unable to find end of rdf:description'); | ||
const rdfDescription = match[0]; | ||
return xmp.indexOf(rdfDescription) + rdfDescription.length - 1; | ||
} | ||
} | ||
exports.XMPEncoder = XMPEncoder; | ||
//# sourceMappingURL=xmp-encoder.js.map |
@@ -30,10 +30,2 @@ import {IGenericMetadata, IBufferLike} from '../utils/types' | ||
interface TagLocation { | ||
start: number | ||
length: number | ||
prefix: string | ||
name: string | ||
value: string | ||
} | ||
export class XMPEncoder { | ||
@@ -73,49 +65,11 @@ public static isWrappedInPacket(xmpData: IBufferLike | string): boolean { | ||
const tagName = key as keyof IGenericMetadata | ||
log(`examining ${tagName}`) | ||
if (!(tagName in xmpTags)) continue | ||
const value = metadata[tagName] | ||
if (tagName === 'DCSubjectBagOfWords') { | ||
const result = XMPEncoder._handleKeywords(xmpData, metadata) | ||
xmpData = result.xmpData | ||
extraLength += result.extraLength | ||
if (!(tagName in xmpTags)) { | ||
log(`skipping ${tagName} which is not an XMP tag`) | ||
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}"`) | ||
const existing = XMPEncoder._findExistingTag(xmpData, tagName) | ||
const replacement = `xmp:${tagName}="${value}"` | ||
if (existing) { | ||
log(`found existing ${tagName} - ${existing.value}`) | ||
const preamble = xmpData.slice(0, existing.start) | ||
const postamble = xmpData.slice(existing.start + existing.length) | ||
const additionalLength = replacement.length - existing.length | ||
xmpData = `${preamble}${replacement}${postamble}` | ||
extraLength += additionalLength | ||
} 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 = `${BASE_NEWLINE}${replacement}` | ||
xmpData = `${preamble}${replacementWithNewline}${postamble}` | ||
extraLength += replacementWithNewline.length | ||
} | ||
const newXmpData = XMPEncoder._processEntry(xmpData, tagName, metadata[tagName]) | ||
extraLength += newXmpData.length - xmpData.length | ||
xmpData = newXmpData | ||
} | ||
@@ -144,55 +98,81 @@ | ||
private static _findExistingTag(xmp: string, tagName: string): TagLocation | undefined { | ||
const regex = new RegExp(`([a-z]+):(${tagName})="(.*?)"`, 'i') | ||
const match = xmp.match(regex) | ||
if (!match) return undefined | ||
const [fullMatch, prefix, name, value] = match | ||
const start = xmp.indexOf(fullMatch) | ||
return {start, length: fullMatch.length, prefix, name, value} | ||
} | ||
private static _processEntry( | ||
xmpData: string, | ||
tagName: keyof IGenericMetadata, | ||
value: string | number | undefined, | ||
): string { | ||
log(`processing ${tagName}`) | ||
const existing = XMPEncoder._findExisting(xmpData, tagName) | ||
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, | ||
// If we are unsetting, branch. | ||
if (typeof value === 'undefined') { | ||
if (!existing) { | ||
// If we didn't have an existing value to begin with, we're done. | ||
log(`${tagName} already missing from XMP, skipping`) | ||
return xmpData | ||
} | ||
log(`unsetting ${tagName}`) | ||
// Remove the existing reference and cleanup whitespace | ||
let preamble = xmpData.slice(0, existing.start) | ||
const postamble = xmpData.slice(existing.start + existing.length) | ||
if (postamble.match(/^(\n|>)/)) preamble = preamble.replace(/\n +$/, '') | ||
return `${preamble}${postamble}` | ||
} | ||
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, | ||
} | ||
log(`writing ${tagName} with value "${value}"`) | ||
const replacement = XMPEncoder._buildReplacement(tagName, value) | ||
if (existing) { | ||
// If we have an existing value, replace the token range with our new payload | ||
log(`found existing ${tagName}`) | ||
const preamble = xmpData.slice(0, existing.start) | ||
const postamble = xmpData.slice(existing.start + existing.length) | ||
return `${preamble}${replacement}${postamble}` | ||
} else { | ||
const rdfEnd = XMPEncoder._findIndexOfRdfDescriptionEnd(xmpData) + 1 | ||
const preamble = xmpData.slice(0, rdfEnd) | ||
const postamble = xmpData.slice(rdfEnd) | ||
// If we don't have an existing value, inject the payload with appropriate whitespace. | ||
log(`did not find existing ${tagName}`) | ||
const insertionIndex = XMPEncoder._findInsertionPoint(xmpData, tagName).start | ||
const preamble = xmpData.slice(0, insertionIndex) | ||
const postamble = xmpData.slice(insertionIndex) | ||
const replacementWithNewline = `${BASE_NEWLINE}${replacement}` | ||
return { | ||
xmpData: `${preamble}${replacementWithNewline}${postamble}`, | ||
extraLength: replacementWithNewline.length, | ||
} | ||
return `${preamble}${replacementWithNewline}${postamble}` | ||
} | ||
} | ||
private static _generateKeywordsPayload(keywords: string[]): string { | ||
private static _findExisting( | ||
xmp: string, | ||
tagName: string, | ||
): {start: number; length: number} | undefined { | ||
const regex = | ||
tagName === 'DCSubjectBagOfWords' | ||
? /<dc:subject>(.|\s)*?<\/dc:subject>/ | ||
: new RegExp(`([a-z]+):(${tagName})="(.*?)"`, 'i') | ||
const match = xmp.match(regex) | ||
if (!match) return | ||
return {start: match.index!, length: match[0].length} | ||
} | ||
private static _findInsertionPoint( | ||
xmp: string, | ||
tagName: keyof IGenericMetadata, | ||
): {start: number} { | ||
const regex = /<rdf:Description[^<]*?>/im | ||
const match = xmp.match(regex) | ||
if (!match) throw new Error('Unable to find end of rdf:description') | ||
const rdfDescription = match[0] | ||
const rdfDescriptionEndIndex = xmp.indexOf(rdfDescription) + rdfDescription.length - 1 | ||
const start = rdfDescriptionEndIndex + (tagName === 'DCSubjectBagOfWords' ? 1 : 0) | ||
return {start} | ||
} | ||
private static _buildReplacement( | ||
tagName: keyof IGenericMetadata, | ||
value: string | number, | ||
): string { | ||
if (tagName !== 'DCSubjectBagOfWords') return `xmp:${tagName}="${value}"` | ||
const keywords = parseKeywords({DCSubjectBagOfWords: value}) | ||
if (!keywords) throw new Error('Invalid keywords payload') | ||
return [ | ||
@@ -206,10 +186,2 @@ `<dc:subject>`, | ||
} | ||
private static _findIndexOfRdfDescriptionEnd(xmp: string): number { | ||
const regex = /<rdf:Description(\s*(\w+:\w+=".*?")\s*)*?>/im | ||
const match = xmp.match(regex) | ||
if (!match) throw new Error('Unable to find end of rdf:description') | ||
const rdfDescription = match[0] | ||
return xmp.indexOf(rdfDescription) + rdfDescription.length - 1 | ||
} | ||
} |
{ | ||
"name": "@eris/exif", | ||
"version": "0.3.1-alpha.1", | ||
"version": "0.3.1-alpha.2", | ||
"description": "Parses EXIF data.", | ||
@@ -49,3 +49,3 @@ "main": "./dist/index.js", | ||
}, | ||
"gitHead": "3ecabcb8242310727ef6c247feae54977438f9d2" | ||
"gitHead": "1be49b5ba3508e530c16a5b221c44d8e4886b3ae" | ||
} |
Sorry, the diff of this file is not supported yet
218143
4033