@eris/exif
Advanced tools
Comparing version 0.4.2-alpha.11 to 0.4.2-alpha.12
@@ -6,2 +6,4 @@ import { IBufferLike, IGenericMetadata } from '../utils/types'; | ||
private _handleMatch; | ||
private _decodeAttributeMetadata; | ||
private _decodeElementMetadata; | ||
private _decodeKeywords; | ||
@@ -8,0 +10,0 @@ extractJPEG(): IBufferLike; |
@@ -6,2 +6,4 @@ "use strict"; | ||
const EXIF_ATTR_REGEX = /:([0-9a-z]+?)="(.*?)"$/i; | ||
const XML_TAG_GLOBAL_REGEX = /<((xmp|exif|tiff):([0-9a-z]+?))>((.|\\s)*?)<\/\1>/gim; | ||
const XML_TAG_REGEX = new RegExp(XML_TAG_GLOBAL_REGEX.source, 'im'); | ||
function isSimpleNumber(s) { | ||
@@ -51,2 +53,18 @@ return /^(([1-9]\d*)|0)$/.test(s); | ||
} | ||
_decodeAttributeMetadata(metadata) { | ||
const matches = this._text.match(EXIF_ATTR_GLOBAL_REGEX); | ||
for (const attribute of matches || []) { | ||
// tslint:disable-next-line | ||
const [_, key, value] = attribute.match(EXIF_ATTR_REGEX) || ['', '', '']; | ||
this._handleMatch(key, value, metadata); | ||
} | ||
} | ||
_decodeElementMetadata(metadata) { | ||
const matches = this._text.match(XML_TAG_GLOBAL_REGEX); | ||
for (const match of matches || []) { | ||
// tslint:disable-next-line | ||
const [_, tagName, namespace, key, value] = match.match(XML_TAG_REGEX) || ['', '', '', '', '']; | ||
this._handleMatch(key, value, metadata); | ||
} | ||
} | ||
_decodeKeywords(genericMetadata) { | ||
@@ -69,8 +87,4 @@ const subjectEl = findXMLTag(this._text, 'dc:subject'); | ||
const metadata = {}; | ||
const matches = this._text.match(EXIF_ATTR_GLOBAL_REGEX); | ||
for (const match of matches || []) { | ||
// @ts-ignore - guaranteed to match from above | ||
const [_, key, value] = match.match(EXIF_ATTR_REGEX); | ||
this._handleMatch(key, value, metadata); | ||
} | ||
this._decodeAttributeMetadata(metadata); | ||
this._decodeElementMetadata(metadata); | ||
this._decodeKeywords(metadata); | ||
@@ -77,0 +91,0 @@ return metadata; |
@@ -8,2 +8,3 @@ import { IGenericMetadata, IBufferLike } from '../utils/types'; | ||
private static _processEntry; | ||
private static _findWithRegex; | ||
private static _findExisting; | ||
@@ -10,0 +11,0 @@ private static _findInsertionPoint; |
@@ -6,2 +6,7 @@ "use strict"; | ||
const keywords_parser_1 = require("../metadata/keywords-parser"); | ||
var XMPMatchType; | ||
(function (XMPMatchType) { | ||
XMPMatchType["Element"] = "element"; | ||
XMPMatchType["Attribute"] = "attribute"; | ||
})(XMPMatchType || (XMPMatchType = {})); | ||
const writableTags = Object.assign({}, tags_1.xmpTags, { DateTimeOriginal: true }); | ||
@@ -102,3 +107,2 @@ const log = log_1.createLogger('xmp-encoder'); | ||
log(`writing ${tagName} with value "${value}"`); | ||
const replacement = XMPEncoder._buildReplacement(tagName, value); | ||
if (existing) { | ||
@@ -109,2 +113,3 @@ // If we have an existing value, replace the token range with our new payload | ||
const postamble = xmpData.slice(existing.start + existing.length); | ||
const replacement = XMPEncoder._buildReplacement(tagName, value, existing.type); | ||
return `${preamble}${replacement}${postamble}`; | ||
@@ -118,2 +123,3 @@ } | ||
const postamble = xmpData.slice(insertionIndex); | ||
const replacement = XMPEncoder._buildReplacement(tagName, value, XMPMatchType.Attribute); | ||
const replacementWithNewline = `${BASE_NEWLINE}${replacement}`; | ||
@@ -123,11 +129,18 @@ return `${preamble}${replacementWithNewline}${postamble}`; | ||
} | ||
static _findExisting(xmp, tagName) { | ||
const regex = tagName === 'DCSubjectBagOfWords' | ||
? /<dc:subject>(.|\s)*?<\/dc:subject>/ | ||
: new RegExp(`([a-z]+):(${tagName})="(.*?)"`, 'i'); | ||
static _findWithRegex(xmp, regex, type) { | ||
const match = xmp.match(regex); | ||
if (!match) | ||
return; | ||
return { start: match.index, length: match[0].length }; | ||
return { start: match.index, length: match[0].length, type }; | ||
} | ||
static _findExisting(xmp, tagName) { | ||
if (tagName === 'DCSubjectBagOfWords') | ||
return this._findWithRegex(xmp, /<dc:subject>(.|\s)*?<\/dc:subject>/, XMPMatchType.Element); | ||
const attributeRegex = new RegExp(`([a-z]+):(${tagName})="(.*?)"`, 'i'); | ||
const attributeMatch = this._findWithRegex(xmp, attributeRegex, XMPMatchType.Attribute); | ||
if (attributeMatch) | ||
return attributeMatch; | ||
const elementRegex = new RegExp(`<([a-z]+:${tagName})(\\s*/>|(.*?)</\\1>)`, 'i'); | ||
return this._findWithRegex(xmp, elementRegex, XMPMatchType.Element); | ||
} | ||
static _findInsertionPoint(xmp, tagName) { | ||
@@ -143,17 +156,22 @@ const regex = /<rdf:Description[^<]*?>/im; | ||
} | ||
static _buildReplacement(tagName, value) { | ||
if (tagName === 'DateTimeOriginal') | ||
return `exif:DateTimeOriginal="${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 [ | ||
`<dc:subject>`, | ||
` <rdf:Bag>`, | ||
...keywords.map(word => ` <rdf:li>${word.replace(/</g, '')}</rdf:li>`), | ||
` </rdf:Bag>`, | ||
`</dc:subject>`, | ||
].join(BASE_NEWLINE); | ||
static _buildReplacement(tagName, value, type) { | ||
if (tagName === 'DCSubjectBagOfWords') { | ||
const keywords = keywords_parser_1.parseKeywords({ DCSubjectBagOfWords: value }); | ||
if (!keywords) | ||
throw new Error('Invalid keywords payload'); | ||
return [ | ||
`<dc:subject>`, | ||
` <rdf:Bag>`, | ||
...keywords.map(word => ` <rdf:li>${word.replace(/</g, '')}</rdf:li>`), | ||
` </rdf:Bag>`, | ||
`</dc:subject>`, | ||
].join(BASE_NEWLINE); | ||
} | ||
const namespace = tagName === 'DateTimeOriginal' ? 'exif' : 'xmp'; | ||
if (type === XMPMatchType.Attribute) { | ||
return `${namespace}:${tagName}="${value}"`; | ||
} | ||
else { | ||
return `<${namespace}:${tagName}>${value}</${namespace}:${tagName}>`; | ||
} | ||
} | ||
@@ -160,0 +178,0 @@ } |
@@ -6,2 +6,4 @@ import {IBufferLike, IGenericMetadata, IFDTagName, XMPTagName} from '../utils/types' | ||
const EXIF_ATTR_REGEX = /:([0-9a-z]+?)="(.*?)"$/i | ||
const XML_TAG_GLOBAL_REGEX = /<((xmp|exif|tiff):([0-9a-z]+?))>((.|\\s)*?)<\/\1>/gim | ||
const XML_TAG_REGEX = new RegExp(XML_TAG_GLOBAL_REGEX.source, 'im') | ||
@@ -58,2 +60,20 @@ function isSimpleNumber(s: string): boolean { | ||
private _decodeAttributeMetadata(metadata: IGenericMetadata): void { | ||
const matches = this._text.match(EXIF_ATTR_GLOBAL_REGEX) | ||
for (const attribute of matches || []) { | ||
// tslint:disable-next-line | ||
const [_, key, value] = attribute.match(EXIF_ATTR_REGEX) || ['', '', ''] | ||
this._handleMatch(key, value, metadata) | ||
} | ||
} | ||
private _decodeElementMetadata(metadata: IGenericMetadata): void { | ||
const matches = this._text.match(XML_TAG_GLOBAL_REGEX) | ||
for (const match of matches || []) { | ||
// tslint:disable-next-line | ||
const [_, tagName, namespace, key, value] = match.match(XML_TAG_REGEX) || ['', '', '', '', ''] | ||
this._handleMatch(key, value, metadata) | ||
} | ||
} | ||
private _decodeKeywords(genericMetadata: IGenericMetadata): void { | ||
@@ -75,10 +95,4 @@ const subjectEl = findXMLTag(this._text, 'dc:subject') | ||
const metadata: IGenericMetadata = {} | ||
const matches = this._text.match(EXIF_ATTR_GLOBAL_REGEX) | ||
for (const match of matches || []) { | ||
// @ts-ignore - guaranteed to match from above | ||
const [_, key, value] = match.match(EXIF_ATTR_REGEX) | ||
this._handleMatch(key, value, metadata) | ||
} | ||
this._decodeAttributeMetadata(metadata) | ||
this._decodeElementMetadata(metadata) | ||
this._decodeKeywords(metadata) | ||
@@ -85,0 +99,0 @@ return metadata |
@@ -6,2 +6,13 @@ import {IGenericMetadata, IBufferLike, XMPTagName} from '../utils/types' | ||
enum XMPMatchType { | ||
Element = 'element', | ||
Attribute = 'attribute', | ||
} | ||
interface IXMPMatch { | ||
start: number | ||
length: number | ||
type: XMPMatchType | ||
} | ||
const writableTags: Record<XMPTagName | 'DateTimeOriginal', boolean> = { | ||
@@ -129,3 +140,2 @@ ...xmpTags, | ||
log(`writing ${tagName} with value "${value}"`) | ||
const replacement = XMPEncoder._buildReplacement(tagName, value) | ||
@@ -137,2 +147,3 @@ if (existing) { | ||
const postamble = xmpData.slice(existing.start + existing.length) | ||
const replacement = XMPEncoder._buildReplacement(tagName, value, existing.type) | ||
return `${preamble}${replacement}${postamble}` | ||
@@ -145,2 +156,3 @@ } else { | ||
const postamble = xmpData.slice(insertionIndex) | ||
const replacement = XMPEncoder._buildReplacement(tagName, value, XMPMatchType.Attribute) | ||
const replacementWithNewline = `${BASE_NEWLINE}${replacement}` | ||
@@ -151,15 +163,22 @@ return `${preamble}${replacementWithNewline}${postamble}` | ||
private static _findExisting( | ||
private static _findWithRegex( | ||
xmp: string, | ||
tagName: string, | ||
): {start: number; length: number} | undefined { | ||
const regex = | ||
tagName === 'DCSubjectBagOfWords' | ||
? /<dc:subject>(.|\s)*?<\/dc:subject>/ | ||
: new RegExp(`([a-z]+):(${tagName})="(.*?)"`, 'i') | ||
regex: RegExp, | ||
type: XMPMatchType, | ||
): IXMPMatch | undefined { | ||
const match = xmp.match(regex) | ||
if (!match) return | ||
return {start: match.index!, length: match[0].length} | ||
return {start: match.index!, length: match[0].length, type} | ||
} | ||
private static _findExisting(xmp: string, tagName: string): IXMPMatch | undefined { | ||
if (tagName === 'DCSubjectBagOfWords') | ||
return this._findWithRegex(xmp, /<dc:subject>(.|\s)*?<\/dc:subject>/, XMPMatchType.Element) | ||
const attributeRegex = new RegExp(`([a-z]+):(${tagName})="(.*?)"`, 'i') | ||
const attributeMatch = this._findWithRegex(xmp, attributeRegex, XMPMatchType.Attribute) | ||
if (attributeMatch) return attributeMatch | ||
const elementRegex = new RegExp(`<([a-z]+:${tagName})(\\s*/>|(.*?)</\\1>)`, 'i') | ||
return this._findWithRegex(xmp, elementRegex, XMPMatchType.Element) | ||
} | ||
private static _findInsertionPoint( | ||
@@ -181,15 +200,23 @@ xmp: string, | ||
value: string | number, | ||
type: XMPMatchType, | ||
): string { | ||
if (tagName === 'DateTimeOriginal') return `exif:DateTimeOriginal="${value}"` | ||
if (tagName !== 'DCSubjectBagOfWords') return `xmp:${tagName}="${value}"` | ||
const keywords = parseKeywords({DCSubjectBagOfWords: value}) | ||
if (!keywords) throw new Error('Invalid keywords payload') | ||
return [ | ||
`<dc:subject>`, | ||
` <rdf:Bag>`, | ||
...keywords.map(word => ` <rdf:li>${word.replace(/</g, '')}</rdf:li>`), | ||
` </rdf:Bag>`, | ||
`</dc:subject>`, | ||
].join(BASE_NEWLINE) | ||
if (tagName === 'DCSubjectBagOfWords') { | ||
const keywords = parseKeywords({DCSubjectBagOfWords: value}) | ||
if (!keywords) throw new Error('Invalid keywords payload') | ||
return [ | ||
`<dc:subject>`, | ||
` <rdf:Bag>`, | ||
...keywords.map(word => ` <rdf:li>${word.replace(/</g, '')}</rdf:li>`), | ||
` </rdf:Bag>`, | ||
`</dc:subject>`, | ||
].join(BASE_NEWLINE) | ||
} | ||
const namespace = tagName === 'DateTimeOriginal' ? 'exif' : 'xmp' | ||
if (type === XMPMatchType.Attribute) { | ||
return `${namespace}:${tagName}="${value}"` | ||
} else { | ||
return `<${namespace}:${tagName}>${value}</${namespace}:${tagName}>` | ||
} | ||
} | ||
} |
{ | ||
"name": "@eris/exif", | ||
"version": "0.4.2-alpha.11", | ||
"version": "0.4.2-alpha.12", | ||
"description": "Parses EXIF data.", | ||
@@ -35,2 +35,3 @@ "main": "./dist/index.js", | ||
"@types/node": "^7.0.16", | ||
"fast-xml-parser": "^3.16.0", | ||
"lodash": "^4.17.4" | ||
@@ -41,3 +42,3 @@ }, | ||
}, | ||
"gitHead": "1963467d76289070ef6e24a7d8e6e89cbc37777b" | ||
"gitHead": "6c902c208d306b02351cb64d2e5e34598215fd74" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
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
245620
4512
4