From ac40e13e3377be6c3dbdf85bb0c1c51f7cd47370 Mon Sep 17 00:00:00 2001 From: Ivan Lopez Date: Sun, 6 May 2018 22:20:56 -0500 Subject: [PATCH 1/5] added support for hyperlinks --- .../paragraph/links/hyperlink-attributes.ts | 13 +++++++ src/file/paragraph/links/hyperlink.spec.ts | 37 +++++++++++++++++++ src/file/paragraph/links/hyperlink.ts | 20 ++++++++++ src/file/paragraph/links/index.ts | 1 + 4 files changed, 71 insertions(+) create mode 100644 src/file/paragraph/links/hyperlink-attributes.ts create mode 100644 src/file/paragraph/links/hyperlink.spec.ts create mode 100644 src/file/paragraph/links/hyperlink.ts create mode 100644 src/file/paragraph/links/index.ts diff --git a/src/file/paragraph/links/hyperlink-attributes.ts b/src/file/paragraph/links/hyperlink-attributes.ts new file mode 100644 index 0000000000..d51f5a8b65 --- /dev/null +++ b/src/file/paragraph/links/hyperlink-attributes.ts @@ -0,0 +1,13 @@ +import { XmlAttributeComponent } from "file/xml-components"; + +export interface IHyperlinkAttributesProperties { + id?: string; + history: number; +} + +export class HyperlinkAttributes extends XmlAttributeComponent { + protected xmlKeys = { + id: "r:id", + history: "w:history", + }; +} diff --git a/src/file/paragraph/links/hyperlink.spec.ts b/src/file/paragraph/links/hyperlink.spec.ts new file mode 100644 index 0000000000..9e3aebc29f --- /dev/null +++ b/src/file/paragraph/links/hyperlink.spec.ts @@ -0,0 +1,37 @@ +import { assert, expect } from "chai"; + +import { Formatter } from "../../../export/formatter"; +import { Utility } from "../../../tests/utility"; +import { Hyperlink } from "./"; + +describe("Hyperlink", () => { + let hyperlink: Hyperlink; + + describe("#constructor()", () => { + it("should create a hyperlink with correct root key", () => { + hyperlink = new Hyperlink("https://example.com", 0); + const newJson = Utility.jsonify(hyperlink); + assert.equal(newJson.rootKey, "w:hyperlink"); + }); + + it("should create a hyperlink with right attributes", () => { + hyperlink = new Hyperlink("https://example.com", 0); + const newJson = Utility.jsonify(hyperlink); + const attributes = { + id: "rId1", + history: 1, + }; + assert.equal(JSON.stringify(newJson.root[0].root), JSON.stringify(attributes)); + }); + + it("should create a hyperlink with a run component", () => { + hyperlink = new Hyperlink("https://example.com", 0); + const tree = new Formatter().format(hyperlink); + expect(tree["w:hyperlink"][1]).to.deep.equal({ + "w:r": [ + { "w:rPr": [{ "w:rStyle": [{ _attr: { "w:val": "Hyperlink"} }] }] }, + { "w:t": [{_attr: {"xml:space": "preserve"}}, "https://example.com"]}, + ]}); + }); + }); +}); diff --git a/src/file/paragraph/links/hyperlink.ts b/src/file/paragraph/links/hyperlink.ts new file mode 100644 index 0000000000..f700ae0a4d --- /dev/null +++ b/src/file/paragraph/links/hyperlink.ts @@ -0,0 +1,20 @@ +// http://officeopenxml.com/WPhyperlink.php +import { XmlComponent } from "file/xml-components"; +import { TextRun } from "../run"; +import { HyperlinkAttributes } from "./hyperlink-attributes"; + +export class Hyperlink extends XmlComponent { + public linkId: number; + + constructor(text: string, relationshipsCount: number) { + super("w:hyperlink"); + + this.linkId = relationshipsCount + 1; + this.root.push(new HyperlinkAttributes({ + id: `rId${this.linkId}`, + history: 1, + })); + this.root.push(new TextRun(text).style("Hyperlink")); + return this; + } +} diff --git a/src/file/paragraph/links/index.ts b/src/file/paragraph/links/index.ts new file mode 100644 index 0000000000..619d1bee30 --- /dev/null +++ b/src/file/paragraph/links/index.ts @@ -0,0 +1 @@ +export * from "./hyperlink"; From 1fd222abeaee4dfce7b9de88a1efe0c96058e844 Mon Sep 17 00:00:00 2001 From: Ivan Lopez Date: Sun, 6 May 2018 22:23:04 -0500 Subject: [PATCH 2/5] create hyperlink style --- src/file/styles/factory.ts | 3 +++ src/file/styles/style/index.ts | 42 +++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/file/styles/factory.ts b/src/file/styles/factory.ts index 0b5faf048d..a45f37d6b4 100644 --- a/src/file/styles/factory.ts +++ b/src/file/styles/factory.ts @@ -9,6 +9,7 @@ import { Heading4Style, Heading5Style, Heading6Style, + HyperlinkStyle, ListParagraph, TitleStyle, } from "./style"; @@ -54,6 +55,8 @@ export class DefaultStylesFactory { // listParagraph.addParagraphProperty(); styles.push(listParagraph); + const hyperLinkStyle = new HyperlinkStyle(); + styles.push(hyperLinkStyle); return styles; } } diff --git a/src/file/styles/style/index.ts b/src/file/styles/style/index.ts index 7436fbb22a..85640a2f29 100644 --- a/src/file/styles/style/index.ts +++ b/src/file/styles/style/index.ts @@ -3,7 +3,7 @@ import * as paragraph from "../../paragraph"; import * as formatting from "../../paragraph/run/formatting"; import { RunProperties } from "../../paragraph/run/properties"; -import { BasedOn, Name, Next, QuickFormat } from "./components"; +import { BasedOn, Name, Next, QuickFormat, UiPriority, UnhideWhenUsed } from "./components"; export interface IStyleAttributes { type?: string; @@ -249,3 +249,43 @@ export class ListParagraph extends ParagraphStyle { this.root.push(new QuickFormat()); } } + +export class CharacterStyle extends Style { + private readonly runProperties: RunProperties; + + constructor(styleId: string, name?: string) { + super({ type: "character", styleId: styleId }, name); + this.runProperties = new RunProperties(); + this.root.push(this.runProperties); + this.root.push(new UiPriority("99")); + this.root.push(new UnhideWhenUsed("")); + } + + public basedOn(parentId: string): CharacterStyle { + this.root.push(new BasedOn(parentId)); + return this; + } + + public addRunProperty(property: XmlComponent): void { + this.runProperties.push(property); + } + + public color(color: string): CharacterStyle { + this.addRunProperty(new formatting.Color(color)); + return this; + } + + public underline(underlineType?: string, color?: string): CharacterStyle { + this.addRunProperty(new formatting.Underline(underlineType, color)); + return this; + } +} + +export class HyperlinkStyle extends CharacterStyle { + constructor() { + super("Hyperlink", "Hyperlink"); + this.basedOn("DefaultParagraphFont") + .color("0563C1") + .underline("single"); + } +} From 195c62f80b85f40b8f90823b407345e9c9c3a3c9 Mon Sep 17 00:00:00 2001 From: Ivan Lopez Date: Sun, 6 May 2018 22:23:35 -0500 Subject: [PATCH 3/5] modify relationships to support external links --- .../relationships/relationship/relationship-attributes.ts | 2 ++ src/file/relationships/relationship/relationship.ts | 8 ++++++-- src/file/relationships/relationships.ts | 6 +++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/file/relationships/relationship/relationship-attributes.ts b/src/file/relationships/relationship/relationship-attributes.ts index 1b23e26977..1cb3e116c0 100644 --- a/src/file/relationships/relationship/relationship-attributes.ts +++ b/src/file/relationships/relationship/relationship-attributes.ts @@ -4,6 +4,7 @@ export interface IRelationshipAttributesProperties { id: string; type: string; target: string; + targetMode?: string; } export class RelationshipAttributes extends XmlAttributeComponent { @@ -11,5 +12,6 @@ export class RelationshipAttributes extends XmlAttributeComponent Date: Sun, 6 May 2018 22:24:16 -0500 Subject: [PATCH 4/5] add hyperlink to paragraph and doc --- src/file/file.ts | 14 +++++++++++++- src/file/paragraph/index.ts | 1 + src/file/paragraph/paragraph.ts | 6 ++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/file/file.ts b/src/file/file.ts index 67393a8c6f..39a9e3d210 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -7,7 +7,7 @@ import { FooterWrapper } from "./footer-wrapper"; import { HeaderWrapper } from "./header-wrapper"; import { Media } from "./media"; import { Numbering } from "./numbering"; -import { Paragraph, PictureRun } from "./paragraph"; +import { Hyperlink, Paragraph, PictureRun } from "./paragraph"; import { Relationships } from "./relationships"; import { Styles } from "./styles"; import { DefaultStylesFactory } from "./styles/factory"; @@ -111,6 +111,18 @@ export class File { return this.document.createDrawing(mediaData); } + public createHyperlink(link: string, text?: string): Hyperlink { + text = text === undefined ? link : text; + const hyperlink = new Hyperlink(text, this.docRelationships.RelationshipCount); + this.docRelationships.createRelationship( + hyperlink.linkId, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink", + link, + "External", + ); + return hyperlink; + } + public get Document(): Document { return this.document; } diff --git a/src/file/paragraph/index.ts b/src/file/paragraph/index.ts index 08d4e487cf..68a1b0b800 100644 --- a/src/file/paragraph/index.ts +++ b/src/file/paragraph/index.ts @@ -2,3 +2,4 @@ export * from "./formatting"; export * from "./paragraph"; export * from "./properties"; export * from "./run"; +export * from "./links"; diff --git a/src/file/paragraph/paragraph.ts b/src/file/paragraph/paragraph.ts index 2c5e007a9b..d8659470b0 100644 --- a/src/file/paragraph/paragraph.ts +++ b/src/file/paragraph/paragraph.ts @@ -13,6 +13,7 @@ import { ISpacingProperties, Spacing } from "./formatting/spacing"; import { Style } from "./formatting/style"; import { CenterTabStop, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop"; import { NumberProperties } from "./formatting/unordered-list"; +import { Hyperlink } from "./links"; import { ParagraphProperties } from "./properties"; export class Paragraph extends XmlComponent { @@ -32,6 +33,11 @@ export class Paragraph extends XmlComponent { return this; } + public addHyperLink(hyperlink: Hyperlink): Paragraph { + this.root.push(hyperlink); + return this; + } + public createTextRun(text: string): TextRun { const run = new TextRun(text); this.addRun(run); From 68cb57aea632b168d652767a4a7e0c4f2adaa016 Mon Sep 17 00:00:00 2001 From: Ivan Lopez Date: Sun, 6 May 2018 23:18:00 -0500 Subject: [PATCH 5/5] fix style issues --- src/file/paragraph/links/hyperlink.spec.ts | 17 ++++++++++------- src/file/paragraph/links/hyperlink.ts | 7 ++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/file/paragraph/links/hyperlink.spec.ts b/src/file/paragraph/links/hyperlink.spec.ts index 9e3aebc29f..e3f0b58c49 100644 --- a/src/file/paragraph/links/hyperlink.spec.ts +++ b/src/file/paragraph/links/hyperlink.spec.ts @@ -7,15 +7,17 @@ import { Hyperlink } from "./"; describe("Hyperlink", () => { let hyperlink: Hyperlink; + beforeEach(() => { + hyperlink = new Hyperlink("https://example.com", 0); + }); + describe("#constructor()", () => { it("should create a hyperlink with correct root key", () => { - hyperlink = new Hyperlink("https://example.com", 0); const newJson = Utility.jsonify(hyperlink); assert.equal(newJson.rootKey, "w:hyperlink"); }); it("should create a hyperlink with right attributes", () => { - hyperlink = new Hyperlink("https://example.com", 0); const newJson = Utility.jsonify(hyperlink); const attributes = { id: "rId1", @@ -25,13 +27,14 @@ describe("Hyperlink", () => { }); it("should create a hyperlink with a run component", () => { - hyperlink = new Hyperlink("https://example.com", 0); const tree = new Formatter().format(hyperlink); - expect(tree["w:hyperlink"][1]).to.deep.equal({ + const runJson = { "w:r": [ - { "w:rPr": [{ "w:rStyle": [{ _attr: { "w:val": "Hyperlink"} }] }] }, - { "w:t": [{_attr: {"xml:space": "preserve"}}, "https://example.com"]}, - ]}); + { "w:rPr": [{ "w:rStyle": [{ _attr: { "w:val": "Hyperlink" } }] }] }, + { "w:t": [{ _attr: { "xml:space": "preserve" } }, "https://example.com"] }, + ], + }; + expect(tree["w:hyperlink"][1]).to.deep.equal(runJson); }); }); }); diff --git a/src/file/paragraph/links/hyperlink.ts b/src/file/paragraph/links/hyperlink.ts index f700ae0a4d..53f72e72bd 100644 --- a/src/file/paragraph/links/hyperlink.ts +++ b/src/file/paragraph/links/hyperlink.ts @@ -1,4 +1,5 @@ // http://officeopenxml.com/WPhyperlink.php + import { XmlComponent } from "file/xml-components"; import { TextRun } from "../run"; import { HyperlinkAttributes } from "./hyperlink-attributes"; @@ -10,11 +11,11 @@ export class Hyperlink extends XmlComponent { super("w:hyperlink"); this.linkId = relationshipsCount + 1; - this.root.push(new HyperlinkAttributes({ + const attributes = new HyperlinkAttributes({ id: `rId${this.linkId}`, history: 1, - })); + }); + this.root.push(attributes); this.root.push(new TextRun(text).style("Hyperlink")); - return this; } }