aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2013-04-08 20:16:05 +0100
committerMichael Williamson <mike@zwobble.org>2013-04-08 20:16:05 +0100
commit2f72e903f5f8bb7ac00c69778be4cb199017617d (patch)
tree0e24cac9cd0f6f3bddc72d6c32f4a2d9192f53dc
parent2ea1d69fe5821a1e4f28e7c411e5bf41a498b2fa (diff)
Move DocumentReader to DocumentXmlReader in its own module
-rw-r--r--lib/document-xml-reader.js158
-rw-r--r--lib/docx-reader.js176
-rw-r--r--lib/results.js26
-rw-r--r--tests/document-xml-reader.tests.js122
-rw-r--r--tests/docx-reader.tests.js113
5 files changed, 309 insertions, 286 deletions
diff --git a/lib/document-xml-reader.js b/lib/document-xml-reader.js
new file mode 100644
index 0000000..ea15204
--- /dev/null
+++ b/lib/document-xml-reader.js
@@ -0,0 +1,158 @@
+exports.DocumentXmlReader = DocumentXmlReader;
+
+var q = require("q");
+var _ = require("underscore");
+var path = require("path");
+
+var documents = require("./documents");
+var Result = require("./results").Result;
+var warning = require("./results").warning;
+
+
+function DocumentXmlReader(relationships, docxFile) {
+ function convertXmlToDocument(documentXml) {
+ var body = documentXml.root.first("w:body");
+
+ var result = readXmlElements(body.children)
+ .map(function(children) {
+ return new documents.Document(children);
+ });
+ result.document = result.value;
+ return result;
+ }
+
+ function readXmlElements(elements) {
+ var results = elements.map(readXmlElement);
+ var values = _.pluck(results, "value").filter(notNull);
+ var messages = _.flatten(_.pluck(results, "messages"), true);
+ return new Result(values, messages);
+ }
+
+ function readXmlElement(element) {
+ if (element.type === "element") {
+ var handler = xmlElementReaders[element.name];
+ if (handler) {
+ return handler(element);
+ } else if (!Object.prototype.hasOwnProperty.call(ignoreElements, element.name)) {
+ return new Result(
+ null,
+ [warning("An unrecognised element was ignored: " + element.name)]
+ );
+ }
+ }
+ return new Result(null);
+ }
+
+ var xmlElementReaders = {
+ "w:p": function(element) {
+ return readXmlElements(element.children)
+ .map(function(children) {
+ var properties = _.find(children, isParagraphProperties);
+
+ return new documents.Paragraph(
+ children.filter(negate(isParagraphProperties)),
+ properties
+ );
+ });
+ },
+ "w:pPr": function(element) {
+ var properties = {
+ type: "paragraphProperties"
+ };
+
+ var styleElement = element.first("w:pStyle");
+ if (styleElement) {
+ properties.styleName = styleElement.attributes["w:val"];
+ }
+
+ return new Result(properties);
+ },
+ "w:r": function(element) {
+ return readXmlElements(element.children)
+ .map(function(children) {
+ var properties = _.find(children, isRunProperties);
+
+ return new documents.Run(
+ children.filter(negate(isRunProperties)),
+ properties
+ );
+ });
+ },
+ "w:rPr": function(element) {
+ var properties = {
+ type: "runProperties"
+ };
+
+ var styleElement = element.first("w:rStyle");
+ if (styleElement) {
+ properties.styleName = styleElement.attributes["w:val"];
+ }
+ properties.isBold = !!element.first("w:b");
+ properties.isItalic = !!element.first("w:i");
+
+ return new Result(properties);
+ },
+ "w:t": function(element) {
+ return new Result(new documents.Text(element.children[0].value));
+ },
+ "w:hyperlink": function(element) {
+ var relationshipId = element.attributes["r:id"];
+ var href = relationships[relationshipId].target;
+ return readXmlElements(element.children)
+ .map(function(children) {
+ return new documents.Hyperlink(children, {href: href});
+ });
+ },
+
+ "w:drawing": function(element) {
+ return readXmlElement(element.first("wp:inline"));
+ },
+ "wp:inline": function(element) {
+ var relationshipId = element
+ .first("a:graphic")
+ .first("a:graphicData")
+ .first("pic:pic")
+ .first("pic:blipFill")
+ .first("a:blip")
+ .attributes["r:embed"];
+ var imagePath = relationships[relationshipId].target;
+ var readImage = function() {
+ return docxFile.read(path.join("word", imagePath));
+ };
+ var altText = element.first("wp:docPr").attributes.descr;
+
+ var image = documents.Image(readImage, altText)
+ return new Result(image);
+ }
+ };
+ return {
+ convertXmlToDocument: convertXmlToDocument,
+ readXmlElement: readXmlElement
+ };
+}
+
+var ignoreElements = {
+ "w:bookmarkStart": true,
+ "w:bookmarkEnd": true,
+ "w:sectPr": true,
+ "w:proofErr": true,
+ "w:lastRenderedPageBreak": true
+};
+
+function notNull(value) {
+ return value !== null;
+}
+
+function isParagraphProperties(element) {
+ return element.type === "paragraphProperties";
+}
+
+function isRunProperties(element) {
+ return element.type === "runProperties";
+}
+
+function negate(predicate) {
+ return function(value) {
+ return !predicate(value);
+ };
+}
diff --git a/lib/docx-reader.js b/lib/docx-reader.js
index 23a3e67..ecaa10c 100644
--- a/lib/docx-reader.js
+++ b/lib/docx-reader.js
@@ -1,5 +1,4 @@
exports.read = read;
-exports.DocumentReader = DocumentReader;
var q = require("q");
var _ = require("underscore");
@@ -8,6 +7,8 @@ var path = require("path");
var xmlreader = require("./xmlreader");
var documents = require("./documents");
+var DocumentXmlReader = require("./document-xml-reader").DocumentXmlReader;
+var Result = require("./results").Result;
function read(docxFile) {
@@ -40,7 +41,7 @@ function read(docxFile) {
readXmlFile(docxFile, "word/document.xml")
]).spread(function(relationshipsXml, documentXml) {
var relationships = relationshipsXml ? readRelationships(relationshipsXml) : {};
- var reader = new DocumentReader(relationships, docxFile);
+ var reader = new DocumentXmlReader(relationships, docxFile);
return reader.convertXmlToDocument(documentXml);
});
}
@@ -66,177 +67,6 @@ function readRelationships(relationshipsXml) {
return relationships;
}
-function DocumentReader(relationships, docxFile) {
- function convertXmlToDocument(documentXml) {
- var body = documentXml.root.first("w:body");
-
- var result = readXmlElements(body.children)
- .map(function(children) {
- return new documents.Document(children);
- });
- result.document = result.value;
- return result;
- }
-
- function readXmlElements(elements) {
- var results = elements.map(readXmlElement);
- var values = _.pluck(results, "value").filter(notNull);
- var messages = _.flatten(_.pluck(results, "messages"), true);
- return new Result(values, messages);
- }
-
- function readXmlElement(element) {
- if (element.type === "element") {
- var handler = xmlElementReaders[element.name];
- if (handler) {
- return handler(element);
- } else if (!Object.prototype.hasOwnProperty.call(ignoreElements, element.name)) {
- return new Result(
- null,
- [warning("An unrecognised element was ignored: " + element.name)]
- );
- }
- }
- return new Result(null);
- }
-
- var xmlElementReaders = {
- "w:p": function(element) {
- return readXmlElements(element.children)
- .map(function(children) {
- var properties = _.find(children, isParagraphProperties);
-
- return new documents.Paragraph(
- children.filter(negate(isParagraphProperties)),
- properties
- );
- });
- },
- "w:pPr": function(element) {
- var properties = {
- type: "paragraphProperties"
- };
-
- var styleElement = element.first("w:pStyle");
- if (styleElement) {
- properties.styleName = styleElement.attributes["w:val"];
- }
-
- return new Result(properties);
- },
- "w:r": function(element) {
- return readXmlElements(element.children)
- .map(function(children) {
- var properties = _.find(children, isRunProperties);
-
- return new documents.Run(
- children.filter(negate(isRunProperties)),
- properties
- );
- });
- },
- "w:rPr": function(element) {
- var properties = {
- type: "runProperties"
- };
-
- var styleElement = element.first("w:rStyle");
- if (styleElement) {
- properties.styleName = styleElement.attributes["w:val"];
- }
- properties.isBold = !!element.first("w:b");
- properties.isItalic = !!element.first("w:i");
-
- return new Result(properties);
- },
- "w:t": function(element) {
- return new Result(new documents.Text(element.children[0].value));
- },
- "w:hyperlink": function(element) {
- var relationshipId = element.attributes["r:id"];
- var href = relationships[relationshipId].target;
- return readXmlElements(element.children)
- .map(function(children) {
- return new documents.Hyperlink(children, {href: href});
- });
- },
-
- "w:drawing": function(element) {
- return readXmlElement(element.first("wp:inline"));
- },
- "wp:inline": function(element) {
- var relationshipId = element
- .first("a:graphic")
- .first("a:graphicData")
- .first("pic:pic")
- .first("pic:blipFill")
- .first("a:blip")
- .attributes["r:embed"];
- var imagePath = relationships[relationshipId].target;
- var readImage = function() {
- return docxFile.read(path.join("word", imagePath));
- };
- var altText = element.first("wp:docPr").attributes.descr;
-
- var image = documents.Image(readImage, altText)
- return new Result(image);
- }
- };
- return {
- convertXmlToDocument: convertXmlToDocument,
- readXmlElement: readXmlElement
- };
-}
-
-var ignoreElements = {
- "w:bookmarkStart": true,
- "w:bookmarkEnd": true,
- "w:sectPr": true,
- "w:proofErr": true,
- "w:lastRenderedPageBreak": true
-};
-
function parseXml(string) {
return xmlreader.read(string);
}
-
-function notNull(value) {
- return value !== null;
-}
-
-function isParagraphProperties(element) {
- return element.type === "paragraphProperties";
-}
-
-function isRunProperties(element) {
- return element.type === "runProperties";
-}
-
-function negate(predicate) {
- return function(value) {
- return !predicate(value);
- };
-}
-
-function Result(value, messages) {
- this.value = value;
- this.messages = messages || [];
-}
-
-Result.prototype.map = function(func) {
- return new Result(func(this.value), this.messages);
-};
-
-Result.prototype.mapThen = function(func) {
- var messages = this.messages;
- return func(this.value).then(function(resultValue) {
- return new Result(resultValue, messages);
- });
-};
-
-function warning(message) {
- return {
- type: "warning",
- message: message
- };
-}
diff --git a/lib/results.js b/lib/results.js
new file mode 100644
index 0000000..5bd81c0
--- /dev/null
+++ b/lib/results.js
@@ -0,0 +1,26 @@
+exports.Result = Result;
+exports.warning = warning;
+
+
+function Result(value, messages) {
+ this.value = value;
+ this.messages = messages || [];
+}
+
+Result.prototype.map = function(func) {
+ return new Result(func(this.value), this.messages);
+};
+
+Result.prototype.mapThen = function(func) {
+ var messages = this.messages;
+ return func(this.value).then(function(resultValue) {
+ return new Result(resultValue, messages);
+ });
+};
+
+function warning(message) {
+ return {
+ type: "warning",
+ message: message
+ };
+}
diff --git a/tests/document-xml-reader.tests.js b/tests/document-xml-reader.tests.js
new file mode 100644
index 0000000..2f406f8
--- /dev/null
+++ b/tests/document-xml-reader.tests.js
@@ -0,0 +1,122 @@
+var assert = require("assert");
+
+var DocumentXmlReader = require("../lib/document-xml-reader").DocumentXmlReader;
+var documents = require("../lib/documents");
+var XmlElement = require("../lib/xmlreader").Element;
+
+var testing = require("./testing");
+var test = testing.test;
+var createFakeDocxFile = testing.createFakeDocxFile;
+
+function readXmlElement(element) {
+ return new DocumentXmlReader({}).readXmlElement(element);
+}
+
+describe("readElement", function() {
+ test("reads styles from run properties", function() {
+ var runStyleXml = new XmlElement("w:rStyle", {"w:val": "Emphasis"});
+ var runPropertiesXml = createRunPropertiesXml([runStyleXml]);
+ var result = readXmlElement(runPropertiesXml);
+ assert.equal(result.value.styleName, "Emphasis");
+ });
+
+ test("isBold is false if bold element is not present", function() {
+ var runPropertiesXml = createRunPropertiesXml([]);
+ var result = readXmlElement(runPropertiesXml);
+ assert.equal(result.value.isBold, false);
+ });
+
+ test("isBold is true if bold element is present", function() {
+ var boldXml = new XmlElement("w:b");
+ var runPropertiesXml = createRunPropertiesXml([boldXml]);
+ var result = readXmlElement(runPropertiesXml);
+ assert.equal(result.value.isBold, true);
+ });
+
+ test("isItalic is false if bold element is not present", function() {
+ var runPropertiesXml = createRunPropertiesXml([]);
+ var result = readXmlElement(runPropertiesXml);
+ assert.equal(result.value.isItalic, false);
+ });
+
+ test("isItalic is true if bold element is present", function() {
+ var italicXml = new XmlElement("w:i");
+ var runPropertiesXml = createRunPropertiesXml([italicXml]);
+ var result = readXmlElement(runPropertiesXml);
+ assert.equal(result.value.isItalic, true);
+ });
+
+ test("run properties are attached to run", function() {
+ var runStyleXml = new XmlElement("w:rStyle", {"w:val": "Emphasis"});
+ var runPropertiesXml = new XmlElement("w:rPr", {}, [runStyleXml]);
+ var runXml = new XmlElement("w:r", {}, [runPropertiesXml]);
+ var result = readXmlElement(runXml);
+ assert.equal(result.value.properties.styleName, "Emphasis");
+ });
+
+ test("run properties not included as child of run", function() {
+ var runStyleXml = new XmlElement("w:rStyle", {"w:val": "Emphasis"});
+ var runPropertiesXml = new XmlElement("w:rPr", {}, [runStyleXml]);
+ var runXml = new XmlElement("w:r", {}, [runPropertiesXml]);
+ var result = readXmlElement(runXml);
+ assert.deepEqual(result.value.children, []);
+ });
+
+ test("emits warning on unrecognised element", function() {
+ var unrecognisedElement = new XmlElement("w:not-an-element");
+ var result = readXmlElement(unrecognisedElement);
+ assert.deepEqual(
+ result.messages,
+ [{
+ type: "warning",
+ message: "An unrecognised element was ignored: w:not-an-element"
+ }]
+ );
+ });
+
+ test("w:bookmarkStart is ignored without warning", function() {
+ var ignoredElement = new XmlElement("w:bookmarkStart");
+ var result = readXmlElement(ignoredElement);
+ assert.deepEqual(result.messages, []);
+ assert.equal(null, result.value);
+ });
+
+ test("can read inline pictures", function() {
+ var drawing = new XmlElement("w:drawing", {}, [
+ new XmlElement("wp:inline", {}, [
+ new XmlElement("wp:docPr", {descr: "It's a hat"}),
+ new XmlElement("a:graphic", {}, [
+ new XmlElement("a:graphicData", {}, [
+ new XmlElement("pic:pic", {}, [
+ new XmlElement("pic:blipFill", {}, [
+ new XmlElement("a:blip", {"r:embed": "rId5"})
+ ])
+ ])
+ ])
+ ])
+ ])
+ ]);
+
+ var imageBuffer = new Buffer("Not an image at all!");
+ var reader = new DocumentXmlReader(
+ {
+ "rId5": {target: "media/hat.png"}
+ },
+ createFakeDocxFile({
+ "word/media/hat.png": imageBuffer
+ })
+ );
+ var result = reader.readXmlElement(drawing);
+ assert.deepEqual(result.messages, []);
+ assert.equal("image", result.value.type);
+ assert.equal(result.value.altText, "It's a hat");
+ return result.value.read()
+ .then(function(readValue) {
+ assert.equal(readValue, imageBuffer)
+ });
+ });
+});
+
+function createRunPropertiesXml(children) {
+ return new XmlElement("w:rPr", {}, children);
+}
diff --git a/tests/docx-reader.tests.js b/tests/docx-reader.tests.js
index 7211f1a..0436d87 100644
--- a/tests/docx-reader.tests.js
+++ b/tests/docx-reader.tests.js
@@ -51,116 +51,3 @@ describe("docx-reader", function() {
});
});
});
-
-function readXmlElement(element) {
- return new docxReader.DocumentReader({}).readXmlElement(element);
-}
-
-describe("readElement", function() {
- test("reads styles from run properties", function() {
- var runStyleXml = new XmlElement("w:rStyle", {"w:val": "Emphasis"});
- var runPropertiesXml = createRunPropertiesXml([runStyleXml]);
- var result = readXmlElement(runPropertiesXml);
- assert.equal(result.value.styleName, "Emphasis");
- });
-
- test("isBold is false if bold element is not present", function() {
- var runPropertiesXml = createRunPropertiesXml([]);
- var result = readXmlElement(runPropertiesXml);
- assert.equal(result.value.isBold, false);
- });
-
- test("isBold is true if bold element is present", function() {
- var boldXml = new XmlElement("w:b");
- var runPropertiesXml = createRunPropertiesXml([boldXml]);
- var result = readXmlElement(runPropertiesXml);
- assert.equal(result.value.isBold, true);
- });
-
- test("isItalic is false if bold element is not present", function() {
- var runPropertiesXml = createRunPropertiesXml([]);
- var result = readXmlElement(runPropertiesXml);
- assert.equal(result.value.isItalic, false);
- });
-
- test("isItalic is true if bold element is present", function() {
- var italicXml = new XmlElement("w:i");
- var runPropertiesXml = createRunPropertiesXml([italicXml]);
- var result = readXmlElement(runPropertiesXml);
- assert.equal(result.value.isItalic, true);
- });
-
- test("run properties are attached to run", function() {
- var runStyleXml = new XmlElement("w:rStyle", {"w:val": "Emphasis"});
- var runPropertiesXml = new XmlElement("w:rPr", {}, [runStyleXml]);
- var runXml = new XmlElement("w:r", {}, [runPropertiesXml]);
- var result = readXmlElement(runXml);
- assert.equal(result.value.properties.styleName, "Emphasis");
- });
-
- test("run properties not included as child of run", function() {
- var runStyleXml = new XmlElement("w:rStyle", {"w:val": "Emphasis"});
- var runPropertiesXml = new XmlElement("w:rPr", {}, [runStyleXml]);
- var runXml = new XmlElement("w:r", {}, [runPropertiesXml]);
- var result = readXmlElement(runXml);
- assert.deepEqual(result.value.children, []);
- });
-
- test("emits warning on unrecognised element", function() {
- var unrecognisedElement = new XmlElement("w:not-an-element");
- var result = readXmlElement(unrecognisedElement);
- assert.deepEqual(
- result.messages,
- [{
- type: "warning",
- message: "An unrecognised element was ignored: w:not-an-element"
- }]
- );
- });
-
- test("w:bookmarkStart is ignored without warning", function() {
- var ignoredElement = new XmlElement("w:bookmarkStart");
- var result = readXmlElement(ignoredElement);
- assert.deepEqual(result.messages, []);
- assert.equal(null, result.value);
- });
-
- test("can read inline pictures", function() {
- var drawing = new XmlElement("w:drawing", {}, [
- new XmlElement("wp:inline", {}, [
- new XmlElement("wp:docPr", {descr: "It's a hat"}),
- new XmlElement("a:graphic", {}, [
- new XmlElement("a:graphicData", {}, [
- new XmlElement("pic:pic", {}, [
- new XmlElement("pic:blipFill", {}, [
- new XmlElement("a:blip", {"r:embed": "rId5"})
- ])
- ])
- ])
- ])
- ])
- ]);
-
- var imageBuffer = new Buffer("Not an image at all!");
- var reader = new docxReader.DocumentReader(
- {
- "rId5": {target: "media/hat.png"}
- },
- createFakeDocxFile({
- "word/media/hat.png": imageBuffer
- })
- );
- var result = reader.readXmlElement(drawing);
- assert.deepEqual(result.messages, []);
- assert.equal("image", result.value.type);
- assert.equal(result.value.altText, "It's a hat");
- return result.value.read()
- .then(function(readValue) {
- assert.equal(readValue, imageBuffer)
- });
- });
-});
-
-function createRunPropertiesXml(children) {
- return new XmlElement("w:rPr", {}, children);
-}