Skip to content

Commit f73b94d

Browse files
committed
Read both attribute and child node properties. Formatting fixes.
1 parent 262491e commit f73b94d

File tree

1 file changed

+114
-89
lines changed

1 file changed

+114
-89
lines changed

js/load-image-xmp.js

Lines changed: 114 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,139 @@
1-
; (function (factory) {
2-
'use strict'
3-
if (typeof define === 'function' && define.amd) {
4-
// Register as an anonymous AMD module:
5-
define(['./load-image', './load-image-meta'], factory)
6-
} else if (typeof module === 'object' && module.exports) {
7-
factory(require('./load-image'), require('./load-image-meta'))
8-
} else {
9-
// Browser globals:
10-
factory(window.loadImage)
1+
;(function (factory) {
2+
'use strict'
3+
if (typeof define === 'function' && define.amd) {
4+
// Register as an anonymous AMD module:
5+
define(['./load-image', './load-image-meta'], factory)
6+
} else if (typeof module === 'object' && module.exports) {
7+
factory(require('./load-image'), require('./load-image-meta'))
8+
} else {
9+
// Browser globals:
10+
factory(window.loadImage)
11+
}
12+
}(function (loadImage) {
13+
'use strict'
14+
15+
loadImage.XmpMap = function () {
16+
return this
17+
};
18+
19+
loadImage.XmpMap.prototype.get = function (id) {
20+
return this[id]
21+
}
22+
23+
loadImage.parseXmpData = function (dataView, offset, length, data, options) {
24+
if (options.disableExif) {
25+
return
1126
}
12-
} (function (loadImage) {
13-
'use strict'
1427

15-
loadImage.XmpMap = function () {
16-
return this
17-
};
28+
// Check for the ASCII code for 'http' (0x68747470). Note that the actual
29+
// XMP identifier is quite long: 'http://ns.adobe.com/xap/1.0/\x00'
30+
var offsetNamespace = 4;
31+
var lengthNamespace = 29;
32+
var arrNamespace = [];
33+
for (var i = 0; i < lengthNamespace; i++) {
34+
arrNamespace.push(String.fromCharCode(dataView.getUint8(offset + offsetNamespace + i)));
35+
}
1836

19-
loadImage.XmpMap.prototype.get = function (id) {
20-
return this[id];
37+
var namespace = arrNamespace.join('');
38+
if (namespace !== 'http://ns.adobe.com/xap/1.0/\0') {
39+
// No XMP data.
40+
return
2141
}
2242

23-
loadImage.parseXmpData = function (dataView, offset, length, data, options) {
24-
if (options.disableExif) {
25-
return;
26-
}
43+
var offsetLp = 2; // Offset to the packet length field.
44+
var packetLength = dataView.getUint16(offset + offsetLp);
2745

28-
// Check for the ASCII code for "http" (0x68747470). Note that the actual XMP identifier is quite long: "http://ns.adobe.com/xap/1.0/\x00"
29-
var offsetNamespace = 4;
30-
var lengthNamespace = 29;
31-
var arrNamespace = [];
32-
for (var i = 0; i < lengthNamespace; i++) {
33-
arrNamespace.push(String.fromCharCode(dataView.getUint8(offset + offsetNamespace + i)));
34-
}
46+
var lengthLp = 2;
47+
packetLength -= (lengthLp + lengthNamespace); // The packet length is a little misleading it includes the length of Lp and Namespace, remove those two to get the actual packet length.
3548

36-
var namespace = arrNamespace.join("");
37-
if (namespace !== "http://ns.adobe.com/xap/1.0/\0") {
38-
// No XMP data.
39-
return;
40-
}
49+
var maxPacketLength = 65503;
50+
if (maxPacketLength < packetLength) {
51+
// The length of the packet is not valid. Note that XMP does allow
52+
// multiple packets but they are unlikely and NOT currently supported here.
53+
return
54+
}
4155

42-
var offsetLp = 2; // Offset to the packet length field.
43-
var packetLength = dataView.getUint16(offset + offsetLp);
56+
// According to Adobe's document
57+
// (http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf)
58+
// an EXIF encoding of XMP will always be UTF-8. No need to check for
59+
// bytes per character or endianness.
4460

45-
var lengthLp = 2;
46-
packetLength -= (lengthLp + lengthNamespace); // The packet length is a little misleading it includes the length of Lp and Namespace, remove those two to get the actual packet length.
61+
// Find the start of packet data.
62+
var offsetPacket = offsetNamespace + lengthNamespace;
63+
var arrPacketData = [];
64+
for (var j = 0; j < packetLength; j++) {
65+
arrPacketData.push(String.fromCharCode(dataView.getUint8(offset + offsetPacket + j)));
66+
}
67+
var xml = arrPacketData.join('');
4768

48-
var maxPacketLength = 65503;
49-
if (maxPacketLength < packetLength) {
50-
// The length of the packet is not valid. Note that XMP does allow multiple packets but they are unlikely and NOT currently supported
51-
return;
52-
}
69+
loadImage.XmpMap.prototype.getXml = function (id) {
70+
return xml
71+
}
5372

54-
// According to Adobe's document
55-
// (http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf)
56-
// an EXIF encoding of XMP will always be UTF-8. No need to check for
57-
// bytes per character or endianness.
73+
data.xmp = new loadImage.XmpMap();
5874

59-
// Find the start of packet data.
60-
var offsetPacket = offsetNamespace + lengthNamespace;
61-
var arrPacketData = [];
62-
for (var j = 0; j < packetLength; j++) {
63-
arrPacketData.push(String.fromCharCode(dataView.getUint8(offset + offsetPacket + j)));
64-
}
65-
var xml = arrPacketData.join("");
75+
// Create an XMLDocument and extract the attributes from the
76+
// Description element.
77+
var xmlDocument = new DOMParser().parseFromString(xml, 'application/xml');
6678

67-
loadImage.XmpMap.prototype.getXml = function (id) {
68-
return xml;
69-
}
79+
var document = xmlDocument.documentElement;
80+
if (!document) {
81+
return
82+
}
7083

71-
data.xmp = new loadImage.XmpMap();
84+
var xpath = "/*[namespace-uri()='adobe:ns:meta/' and local-name()='xmpmeta']/*[namespace-uri()='http://www.w3.org/1999/02/22-rdf-syntax-ns#' and local-name()='RDF']//*[namespace-uri()='http://www.w3.org/1999/02/22-rdf-syntax-ns#' and local-name()='Description']";
85+
var xpathResult;
86+
try {
87+
xpathResult = xmlDocument.evaluate(xpath, document, xmlDocument.createNSResolver(document), XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
88+
} catch (err) {
89+
console.log("Error selecting Description node: '" + err.message || JSON.stringify(err) + "'.");
90+
}
7291

73-
// Create an XMLDocument and extract the attributes from the
74-
// Description element.
75-
var xmlDocument = new DOMParser().parseFromString(xml, "application/xml");
92+
if (!xpathResult) {
93+
return
94+
}
7695

77-
var document = xmlDocument.documentElement;
78-
if (!document) {
79-
return;
80-
}
96+
if (xpathResult.resultType !== XPathResult.ORDERED_NODE_ITERATOR_TYPE) {
97+
console.log('xpathResult type ' + xpathResult.resultType + ' is not supported.');
98+
return
99+
}
81100

82-
var xpath = "/*[namespace-uri()='adobe:ns:meta/' and local-name()='xmpmeta']/*[namespace-uri()='http://www.w3.org/1999/02/22-rdf-syntax-ns#' and local-name()='RDF']/*[namespace-uri()='http://www.w3.org/1999/02/22-rdf-syntax-ns#' and local-name()='Description']";
83-
var xpathResult;
84-
try {
85-
xpathResult = xmlDocument.evaluate(xpath, document, xmlDocument.createNSResolver(document), XPathResult.FIRST_ORDERED_NODE_TYPE, null);
86-
} catch (err) {
87-
console.log("Error selecting Description node: '" + err.message || JSON.stringify(err) + "'.");
101+
// There can be multiple Description nodes.
102+
var node = xpathResult.iterateNext();
103+
var attr;
104+
while (node) {
105+
// The description node can have the XMP information stored as attributes
106+
// of the node (if they are "simple"") or as child nodes. Get the
107+
// attributes.
108+
// http://www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart1.pdf
109+
for (var k = 0; k < node.attributes.length; k++) {
110+
attr = node.attributes[k];
111+
112+
// Don't include namespace definitions.
113+
if (attr.prefix === 'xmlns' || attr.localName === "about") {
114+
continue
88115
}
89116

90-
if (!xpathResult) {
91-
return;
92-
}
117+
data.xmp[attr.localName] = attr.value;
118+
}
93119

94-
if (xpathResult.resultType !== XPathResult.FIRST_ORDERED_NODE_TYPE) {
95-
console.log("xpathResult type " + xpathResult.resultType + " is not supported.");
96-
return;
97-
}
120+
// Check for properties stored as child nodes.
121+
var childNode;
122+
for (var m = 0; m < node.children.length; m++) {
123+
childNode = node.children[m];
98124

99-
var node = xpathResult.singleNodeValue;
100-
var attr;
101-
for (var k = 0; k < node.attributes.length; k++) {
102-
attr = node.attributes[k];
125+
// If the Description child has its own children the child is a
126+
// "complex" property and it will be ignored.
127+
if (childNode.children) {
128+
continue;
129+
}
103130

104-
// Don't include namespace definitions.
105-
if (attr.prefix === "xmlns"){
106-
continue;
107-
}
131+
data.xmp[childNode.localName] = childNode.textContent;
132+
}
108133

109-
data.xmp[attr.localName] = attr.value;
110-
}
134+
node = xpathResult.iterateNext();
111135
}
136+
}
112137

113-
loadImage.metaDataParsers.jpeg[0xffe1].push(loadImage.parseXmpData);
114-
}))
138+
loadImage.metaDataParsers.jpeg[0xffe1].push(loadImage.parseXmpData);
139+
}))

0 commit comments

Comments
 (0)