summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Lubbe <git@lubbe.org>2015-01-23 18:24:23 +0100
committerMichael Williamson <mike@zwobble.org>2015-02-09 20:31:51 +0000
commit4d4d9e669b389be294373cf3c9bd4727dd5f32af (patch)
treed66a47574dfea6c24328aa296cb3b67f2d7890f2
parent37cdbc47dc105a3c745e0f718843e68cf1741aed (diff)
Add support for internal hyperlinks
-rw-r--r--lib/document-to-html.js15
-rw-r--r--lib/documents.js14
-rw-r--r--lib/docx/document-xml-reader.js12
-rw-r--r--tests/document-to-html.tests.js34
-rw-r--r--tests/docx/document-xml-reader.tests.js22
5 files changed, 84 insertions, 13 deletions
diff --git a/lib/document-to-html.js b/lib/document-to-html.js
index c068fba..000fc5c 100644
--- a/lib/document-to-html.js
+++ b/lib/document-to-html.js
@@ -234,7 +234,9 @@ function DocumentConversion(options) {
callback();
},
"hyperlink": function(element, html, messages, callback) {
- html.open(htmlPaths.element("a", {href: element.href}));
+ var href = element.anchor ? "#" + element.anchor : element.href;
+
+ html.open(htmlPaths.element("a", {href: href}));
convertElements(element.children, html, messages, function(err) {
if (err) {
callback(err);
@@ -280,6 +282,17 @@ function DocumentConversion(options) {
"lineBreak": function(element, html, messages, callback) {
html.selfClosing(htmlPaths.element("br"));
callback();
+ },
+ "bookmarkStart": function(element, html, messages, callback) {
+ if (element.name === "_GoBack") {
+ return callback();
+ }
+
+ html.open(htmlPaths.element("span", {
+ id: element.name
+ }), {alwaysWrite: true});
+ html.close();
+ callback();
}
};
return {
diff --git a/lib/documents.js b/lib/documents.js
index 78bea1a..10d8646 100644
--- a/lib/documents.js
+++ b/lib/documents.js
@@ -13,7 +13,8 @@ var types = {
table: "table",
tableRow: "tableRow",
tableCell: "tableCell",
- lineBreak: "lineBreak"
+ lineBreak: "lineBreak",
+ bookmarkStart: "bookmarkStart"
};
function Document(children, options) {
@@ -74,7 +75,8 @@ function Hyperlink(children, options) {
return {
type: types.hyperlink,
children: children,
- href: options.href
+ href: options.href,
+ anchor: options.anchor
};
}
@@ -149,6 +151,13 @@ function LineBreak() {
};
}
+function BookmarkStart(options) {
+ return {
+ type: types.bookmarkStart,
+ name: options.name
+ };
+}
+
exports.Document = Document;
exports.Paragraph = Paragraph;
exports.Run = Run;
@@ -163,5 +172,6 @@ exports.Table = Table;
exports.TableRow = TableRow;
exports.TableCell = TableCell;
exports.LineBreak = LineBreak;
+exports.BookmarkStart = BookmarkStart;
exports.verticalAlignment = verticalAlignment;
diff --git a/lib/docx/document-xml-reader.js b/lib/docx/document-xml-reader.js
index e290792..f25e7a3 100644
--- a/lib/docx/document-xml-reader.js
+++ b/lib/docx/document-xml-reader.js
@@ -167,10 +167,13 @@ function DocumentXmlReader(options) {
},
"w:hyperlink": function(element) {
var relationshipId = element.attributes["r:id"];
+ var anchor = element.attributes["w:anchor"];
return readXmlElements(element.children).map(function(children) {
if (relationshipId) {
var href = relationships[relationshipId].target;
return new documents.Hyperlink(children, {href: href});
+ } else if (anchor) {
+ return new documents.Hyperlink(children, {anchor: anchor});
} else {
return children;
}
@@ -195,7 +198,13 @@ function DocumentXmlReader(options) {
return new Result(new documents.LineBreak());
}
},
-
+ "w:bookmarkStart": function(element){
+ var name = element.attributes["w:name"];
+ return readXmlElements(element.children).map(function(){
+ return new documents.BookmarkStart({name: name});
+ });
+ },
+
"w:ins": readChildElements,
"w:smartTag": readChildElements,
"w:drawing": readChildElements,
@@ -245,7 +254,6 @@ var supportedImageTypes = {
};
var ignoreElements = {
- "w:bookmarkStart": true,
"w:bookmarkEnd": true,
"w:sectPr": true,
"w:proofErr": true,
diff --git a/tests/document-to-html.tests.js b/tests/document-to-html.tests.js
index 3f50cbd..4863cf1 100644
--- a/tests/document-to-html.tests.js
+++ b/tests/document-to-html.tests.js
@@ -239,7 +239,18 @@ describe('DocumentConverter', function() {
assert.equal(result.value, '<a href="http://www.example.com">Hello.</a>');
});
});
-
+
+ test('docx hyperlink with anchor is converted to <a>', function() {
+ var hyperlink = new documents.Hyperlink(
+ [runOfText("Hello.")],
+ {anchor: "_Peter"}
+ );
+ var converter = new DocumentConverter();
+ return converter.convertToHtml(hyperlink).then(function(result) {
+ assert.equal(result.value, '<a href="#_Peter">Hello.</a>');
+ });
+ });
+
test('docx tab is converted to tab in HTML', function() {
var tab = new documents.Tab();
var converter = new DocumentConverter();
@@ -405,6 +416,27 @@ describe('DocumentConverter', function() {
assert.equal(result.value.indexOf("<p>Hello.</p>"), 0);
});
});
+
+ test('referenced bookmarks are coverted to anchors', function() {
+ var bookmarkStart = new documents.BookmarkStart({name: "_Peter"});
+ var hyperlink = new documents.Hyperlink(
+ [runOfText("Hello.")],
+ {anchor: "_Peter"}
+ );
+ var converter = new DocumentConverter();
+ var document = new documents.Document([bookmarkStart, hyperlink]);
+ return converter.convertToHtml(document).then(function(result) {
+ assert.equal(result.value, '<span id="_Peter"></span><a href="#_Peter">Hello.</a>');
+ });
+ });
+
+ test('unreferenced bookmarks are not converted', function() {
+ var bookmarkStart = new documents.BookmarkStart({name: "_Unreferenced"});
+ var converter = new DocumentConverter();
+ return converter.convertToHtml(bookmarkStart).then(function(result) {
+ assert.equal(result.value, '');
+ });
+ });
});
function paragraphOfText(text, styleId, styleName) {
diff --git a/tests/docx/document-xml-reader.tests.js b/tests/docx/document-xml-reader.tests.js
index 0b5a75f..35c806b 100644
--- a/tests/docx/document-xml-reader.tests.js
+++ b/tests/docx/document-xml-reader.tests.js
@@ -217,12 +217,12 @@ describe("readXmlElement: ", function() {
}]
);
});
-
- test("w:bookmarkStart is ignored without warning", function() {
- var ignoredElement = new XmlElement("w:bookmarkStart");
- var result = readXmlElement(ignoredElement);
- assert.deepEqual(result.messages, []);
- assert.deepEqual([], result.value);
+
+ test("w:bookmarkStart is read as a bookmarkStart", function() {
+ var bookmarkStart = new XmlElement("w:bookmarkStart", {"w:name": "_Peter", "w:id": "42"});
+ var result = readXmlElement(bookmarkStart);
+ assert.deepEqual(result.value.name, "_Peter");
+ assert.deepEqual(result.value.type, "bookmarkStart");
});
test("can read inline pictures", function() {
@@ -354,7 +354,15 @@ describe("readXmlElement: ", function() {
assert.deepEqual(result.value.href, "http://example.com");
assert.deepEqual(result.value.children[0].type, "run");
});
-
+
+ test("w:hyperlink is read as document hyperlink if it has an anchor", function() {
+ var runXml = new XmlElement("w:r", {}, []);
+ var hyperlinkXml = new XmlElement("w:hyperlink", {"w:anchor": "_Peter"}, [runXml]);
+ var result = readXmlElement(hyperlinkXml);
+ assert.deepEqual(result.value.anchor, "_Peter");
+ assert.deepEqual(result.value.children[0].type, "run");
+ });
+
test("w:hyperlink is ignored if it does not have a relationship ID", function() {
var runXml = new XmlElement("w:r", {}, []);
var hyperlinkXml = new XmlElement("w:hyperlink", {}, [runXml]);