From 8b9ca6ca68fec89926cfb02a96506bbee54a5b2c Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Fri, 26 Jan 2018 23:30:13 +0000 Subject: Find comments part using document relationships --- lib/docx/docx-reader.js | 63 ++++++++++++++++++++++++++++++++---------- lib/zipfile.js | 16 +++++++++-- test/docx/docx-reader.tests.js | 38 +++++++++++++++++++++++++ test/zipfile.tests.js | 9 ++++++ 4 files changed, 110 insertions(+), 16 deletions(-) diff --git a/lib/docx/docx-reader.js b/lib/docx/docx-reader.js index cbeaccd..e6dd798 100644 --- a/lib/docx/docx-reader.js +++ b/lib/docx/docx-reader.js @@ -83,29 +83,64 @@ function read(docxFile, input) { function findPartPaths(docxFile) { return readPackageRelationships(docxFile).then(function(packageRelationships) { - var documentFilename = findDocumentFilename(docxFile, packageRelationships); - return { - mainDocument: documentFilename, - comments: "word/comments.xml", - endnotes: "word/endnotes.xml", - footnotes: "word/footnotes.xml", - numbering: "word/numbering.xml", - styles: "word/styles.xml" - }; + var mainDocumentPath = findPartPath({ + docxFile: docxFile, + relationships: packageRelationships, + relationshipType: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", + basePath: "", + fallbackPath: "word/document.xml" + }); + + if (!docxFile.exists(mainDocumentPath)) { + throw new Error("Could not find main document part. Are you sure this is a valid .docx file?"); + } + + return xmlFileReader({ + filename: relationshipsFilename(mainDocumentPath), + readElement: relationshipsReader.readRelationships, + defaultValue: relationshipsReader.defaultValue + })(docxFile).then(function(documentRelationships) { + function findPartRelatedToMainDocument(options) { + return findPartPath({ + docxFile: docxFile, + relationships: documentRelationships, + relationshipType: options.relationshipType, + basePath: zipfile.splitPath(mainDocumentPath).dirname, + fallbackPath: options.fallbackPath + }); + } + + return { + mainDocument: mainDocumentPath, + comments: findPartRelatedToMainDocument({ + relationshipType: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", + fallbackPath: "word/comments.xml" + }), + endnotes: "word/endnotes.xml", + footnotes: "word/footnotes.xml", + numbering: "word/numbering.xml", + styles: "word/styles.xml" + }; + }); }); } -function findDocumentFilename(docxFile, relationships) { - var officeDocumentType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"; - var targets = relationships.findTargetsByType(officeDocumentType).concat(["word/document.xml"]); +function findPartPath(options) { + var docxFile = options.docxFile; + var relationships = options.relationships; + var relationshipType = options.relationshipType; + var basePath = options.basePath; + var fallbackPath = options.fallbackPath; + + var targets = relationships.findTargetsByType(relationshipType); var normalisedTargets = targets.map(function(target) { - return stripPrefix(target, "/"); + return stripPrefix(zipfile.joinPath(basePath, target), "/"); }); var validTargets = normalisedTargets.filter(function(target) { return docxFile.exists(target); }); if (validTargets.length === 0) { - throw new Error("Could not find main document part. Are you sure this is a valid .docx file?"); + return fallbackPath; } else { return validTargets[0]; } diff --git a/lib/zipfile.js b/lib/zipfile.js index ae51310..8d46d92 100644 --- a/lib/zipfile.js +++ b/lib/zipfile.js @@ -51,7 +51,19 @@ function splitPath(path) { } function joinPath() { - return Array.prototype.filter.call(arguments, function(path) { + var nonEmptyPaths = Array.prototype.filter.call(arguments, function(path) { return path; - }).join("/"); + }); + + var relevantPaths = []; + + nonEmptyPaths.forEach(function(path) { + if (/^\//.test(path)) { + relevantPaths = [path]; + } else { + relevantPaths.push(path); + } + }); + + return relevantPaths.join("/"); } diff --git a/test/docx/docx-reader.tests.js b/test/docx/docx-reader.tests.js index 0556405..0d51f45 100644 --- a/test/docx/docx-reader.tests.js +++ b/test/docx/docx-reader.tests.js @@ -113,5 +113,43 @@ test("part paths", { return docxReader._findPartPaths(docxFile).then(function(partPaths) { assert.equal(partPaths.mainDocument, "word/document.xml"); }); + }, + + "comments part is found using main document relationships": function() { + var docxFile = createFakeDocxFile({ + "_rels/.rels": createPackageRelationships("word/document.xml"), + "word/document.xml": " ", + "word/_rels/document.xml.rels": xml.writeString(xml.element("r:Relationships", {}, [ + xml.element("r:Relationship", { + "Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments", + "Target": "comments2.xml" + }) + ]), relationshipNamespaces), + "word/comments2.xml": " " + }); + return docxReader._findPartPaths(docxFile).then(function(partPaths) { + assert.equal(partPaths.comments, "word/comments2.xml"); + }); + }, + + "word/comments.xml is used as fallback location for comments part": function() { + var docxFile = createFakeDocxFile({ + "_rels/.rels": createPackageRelationships("word/document.xml"), + "word/document.xml": " ", + "word/comments.xml": " " + }); + return docxReader._findPartPaths(docxFile).then(function(partPaths) { + assert.equal(partPaths.comments, "word/comments.xml"); + }); } }); + + +function createPackageRelationships(mainDocumentPath) { + return xml.writeString(xml.element("r:Relationships", {}, [ + xml.element("r:Relationship", { + "Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument", + "Target": mainDocumentPath + }) + ]), relationshipNamespaces); +} diff --git a/test/zipfile.tests.js b/test/zipfile.tests.js index 3087dc8..5383c49 100644 --- a/test/zipfile.tests.js +++ b/test/zipfile.tests.js @@ -39,6 +39,7 @@ test("when path has no forward slashes then splitPath returns empty dirname", fu test("joinPath joins arguments with forward slashes", function() { assert.equal(zipfile.joinPath("a", "b"), "a/b"); assert.equal(zipfile.joinPath("a/b", "c"), "a/b/c"); + assert.equal(zipfile.joinPath("a", "b/c"), "a/b/c"); assert.equal(zipfile.joinPath("/a/b", "c"), "/a/b/c"); }); @@ -48,3 +49,11 @@ test("empty parts are ignored when joining paths", function() { assert.equal(zipfile.joinPath("", "b"), "b"); assert.equal(zipfile.joinPath("a", "", "b"), "a/b"); }); + + +test("when joining paths then absolute paths ignore earlier paths", function() { + assert.equal(zipfile.joinPath("a", "/b"), "/b"); + assert.equal(zipfile.joinPath("a", "/b", "c"), "/b/c"); + assert.equal(zipfile.joinPath("/a", "/b"), "/b"); + assert.equal(zipfile.joinPath("/a"), "/a"); +}); -- cgit v1.2.3