From dfb910defbc46de25e93617568d24b7849749fd4 Mon Sep 17 00:00:00 2001 From: James Montalvo Date: Tue, 1 Oct 2019 12:29:07 -0500 Subject: [PATCH] Add SymbolRun to allow adding symbols inline --- docs/usage/paragraph.md | 10 ++- docs/usage/symbols.md | 53 +++++++++++++ src/file/paragraph/paragraph.ts | 4 +- src/file/paragraph/run/index.ts | 1 + .../run/run-components/symbol.spec.ts | 28 +++++++ .../paragraph/run/run-components/symbol.ts | 20 +++++ src/file/paragraph/run/symbol-run.spec.ts | 76 +++++++++++++++++++ src/file/paragraph/run/symbol-run.ts | 20 +++++ 8 files changed, 207 insertions(+), 5 deletions(-) create mode 100644 docs/usage/symbols.md create mode 100644 src/file/paragraph/run/run-components/symbol.spec.ts create mode 100644 src/file/paragraph/run/run-components/symbol.ts create mode 100644 src/file/paragraph/run/symbol-run.spec.ts create mode 100644 src/file/paragraph/run/symbol-run.ts diff --git a/docs/usage/paragraph.md b/docs/usage/paragraph.md index c770e8c00f..2bf22e7022 100644 --- a/docs/usage/paragraph.md +++ b/docs/usage/paragraph.md @@ -2,7 +2,7 @@ > Everything (text, images, graphs etc) in OpenXML is organised in paragraphs. -!> Paragraphs requires an understanding of [Sections](usage/sections.md). +!> Paragraphs requires an understanding of [Sections](sections.md). You can create `Paragraphs` in the following ways: @@ -16,11 +16,15 @@ const paragraph = new Paragraph("Short hand Hello World"); ### Children Method -This method is useful for adding different `text` with different styles or adding `images` inline. +This method is useful for adding different [text](text.md) with different styles, [symbols](symbols.md), or adding [images](images.md) inline. ```ts const paragraph = new Paragraph({ - children: [new TextRun("Lorem Ipsum Foo Bar"), new TextRun("Hello World")], + children: [ + new TextRun("Lorem Ipsum Foo Bar"), + new TextRun("Hello World"), + new SymbolRun("F071"), + ], }); ``` diff --git a/docs/usage/symbols.md b/docs/usage/symbols.md new file mode 100644 index 0000000000..8b283e9da7 --- /dev/null +++ b/docs/usage/symbols.md @@ -0,0 +1,53 @@ +# Symbol Runs + +!> SymbolRuns require an understanding of [Paragraphs](paragraph.md). + +You can add multiple `symbol runs` in `Paragraphs` along with [text runs](text.md) using the Paragraph's `children` property. + +```ts +import { Paragraph, TextRun, SymbolRun } from "docx"; + +const paragraph = new Paragraph({ + children: [ + new TextRun("This is a checkbox: "), + new SymbolRun("F071") + ], +}); +``` + +## Specifying symbol font + +By default symbol runs will use the `Wingdings` font. To switch fonts, pass an object instead of a string to the `SymbolRun` constructor and specify `char` and `symbolfont` properties: + +```ts +const symbol = new SymbolRun({ + char: "F071", + symbolfont: "Arial", +}); +``` + +## Example symbols + +Symbols are specified by their hexidecimal code. Ref http://officeopenxml.com/WPtextSpecialContent-symbol.php. Below are some examples. + +- `F071`: empty checkbox +- `F043`: thumbs up +- `F04A`: smile +- `F04C`: frown +- `F022`: scissors +- `F0F0`: right arrow +- `F0FE`: checked box + +## Typographical Emphasis + +Symbol runs can have their display modified just like text runs. For example, they can be bolded and italicized: + +```ts +const symbol = new SymbolRun({ + char: "F071", + bold: true, + italics: true, +}); +``` + +See the [text run](text.md) documentation for more info. diff --git a/src/file/paragraph/paragraph.ts b/src/file/paragraph/paragraph.ts index 24c3e1717d..150036707f 100644 --- a/src/file/paragraph/paragraph.ts +++ b/src/file/paragraph/paragraph.ts @@ -15,7 +15,7 @@ import { CenterTabStop, LeaderType, LeftTabStop, MaxRightTabStop, RightTabStop } import { NumberProperties } from "./formatting/unordered-list"; import { Bookmark, Hyperlink, OutlineLevel } from "./links"; import { ParagraphProperties } from "./properties"; -import { PictureRun, Run, SequentialIdentifier, TextRun } from "./run"; +import { PictureRun, Run, SequentialIdentifier, SymbolRun, TextRun } from "./run"; interface ITabStopOptions { readonly position: number; @@ -53,7 +53,7 @@ export interface IParagraphOptions { readonly level: number; readonly custom?: boolean; }; - readonly children?: Array; + readonly children?: Array; } export class Paragraph extends XmlComponent { diff --git a/src/file/paragraph/run/index.ts b/src/file/paragraph/run/index.ts index a30a1d6037..df56d26d17 100644 --- a/src/file/paragraph/run/index.ts +++ b/src/file/paragraph/run/index.ts @@ -1,4 +1,5 @@ export * from "./run"; export * from "./text-run"; +export * from "./symbol-run"; export * from "./picture-run"; export * from "./sequential-identifier"; diff --git a/src/file/paragraph/run/run-components/symbol.spec.ts b/src/file/paragraph/run/run-components/symbol.spec.ts new file mode 100644 index 0000000000..559790577e --- /dev/null +++ b/src/file/paragraph/run/run-components/symbol.spec.ts @@ -0,0 +1,28 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { Symbol } from "./symbol"; + +describe("Symbol", () => { + describe("#constructor", () => { + // Note: if no character is given, the output is a MS Windows logo + it("creates an empty symbol run if no character is given", () => { + const s = new Symbol(); + const f = new Formatter().format(s); + expect(f).to.deep.equal({ "w:sym": { _attr: { "w:char": "", "w:font": "Wingdings" } } }); + }); + + it("creates the provided symbol with default font", () => { + const s = new Symbol("F071"); + const f = new Formatter().format(s); + expect(f).to.deep.equal({ "w:sym": { _attr: { "w:char": "F071", "w:font": "Wingdings" } } }); + }); + + it("creates the provided symbol with the provided font", () => { + const s = new Symbol("F071", "Arial"); + const f = new Formatter().format(s); + expect(f).to.deep.equal({ "w:sym": { _attr: { "w:char": "F071", "w:font": "Arial" } } }); + }); + }); +}); diff --git a/src/file/paragraph/run/run-components/symbol.ts b/src/file/paragraph/run/run-components/symbol.ts new file mode 100644 index 0000000000..dff2e7c39b --- /dev/null +++ b/src/file/paragraph/run/run-components/symbol.ts @@ -0,0 +1,20 @@ +import { XmlAttributeComponent, XmlComponent } from "file/xml-components"; + +interface ISymbolAttributesProperties { + readonly char: string; + readonly symbolfont?: string; +} + +class SymbolAttributes extends XmlAttributeComponent { + protected readonly xmlKeys = { + char: "w:char", + symbolfont: "w:font", + }; +} + +export class Symbol extends XmlComponent { + constructor(char: string = "", symbolfont: string = "Wingdings") { + super("w:sym"); + this.root.push(new SymbolAttributes({ char: char, symbolfont: symbolfont })); + } +} diff --git a/src/file/paragraph/run/symbol-run.spec.ts b/src/file/paragraph/run/symbol-run.spec.ts new file mode 100644 index 0000000000..f3faee8bb0 --- /dev/null +++ b/src/file/paragraph/run/symbol-run.spec.ts @@ -0,0 +1,76 @@ +import { expect } from "chai"; + +import { Formatter } from "export/formatter"; + +import { UnderlineType } from "./underline"; + +import { SymbolRun } from "./symbol-run"; + +describe("SymbolRun", () => { + let run: SymbolRun; + + describe("#constructor()", () => { + it("should create symbol run from text input", () => { + run = new SymbolRun("F071"); + const f = new Formatter().format(run); + expect(f).to.deep.equal({ + "w:r": [{ "w:sym": { _attr: { "w:char": "F071", "w:font": "Wingdings" } } }], + }); + }); + + it("should create symbol run from object input with just 'char' specified", () => { + run = new SymbolRun({ char: "F071" }); + const f = new Formatter().format(run); + expect(f).to.deep.equal({ + "w:r": [{ "w:sym": { _attr: { "w:char": "F071", "w:font": "Wingdings" } } }], + }); + }); + + it("should create symbol run from object input with just 'char' specified", () => { + run = new SymbolRun({ char: "F071", symbolfont: "Arial" }); + const f = new Formatter().format(run); + expect(f).to.deep.equal({ + "w:r": [{ "w:sym": { _attr: { "w:char": "F071", "w:font": "Arial" } } }], + }); + }); + + it("should add other standard run properties", () => { + run = new SymbolRun({ + char: "F071", + symbolfont: "Arial", + italics: true, + bold: true, + underline: { + color: "red", + type: UnderlineType.DOUBLE, + }, + color: "green", + size: 40, + highlight: "yellow", + }); + + const f = new Formatter().format(run); + expect(f).to.deep.equal({ + "w:r": [ + { + "w:rPr": [ + { "w:b": { _attr: { "w:val": true } } }, + { "w:bCs": { _attr: { "w:val": true } } }, + { "w:i": { _attr: { "w:val": true } } }, + { "w:iCs": { _attr: { "w:val": true } } }, + { "w:u": { _attr: { "w:val": "double", "w:color": "red" } } }, + { "w:color": { _attr: { "w:val": "green" } } }, + { "w:sz": { _attr: { "w:val": 40 } } }, + { "w:szCs": { _attr: { "w:val": 40 } } }, + { "w:highlight": { _attr: { "w:val": "yellow" } } }, + { "w:highlightCs": { _attr: { "w:val": "yellow" } } }, + ], + }, + { + "w:sym": { _attr: { "w:char": "F071", "w:font": "Arial" } }, + }, + ], + }); + }); + }); +}); diff --git a/src/file/paragraph/run/symbol-run.ts b/src/file/paragraph/run/symbol-run.ts new file mode 100644 index 0000000000..7291d11521 --- /dev/null +++ b/src/file/paragraph/run/symbol-run.ts @@ -0,0 +1,20 @@ +import { IRunOptions, Run } from "./run"; +import { Symbol } from "./run-components/symbol"; + +export interface ISymbolRunOptions extends IRunOptions { + readonly char: string; + readonly symbolfont?: string; +} + +export class SymbolRun extends Run { + constructor(options: ISymbolRunOptions | string) { + if (typeof options === "string") { + super({}); + this.root.push(new Symbol(options)); + return; + } + + super(options); + this.root.push(new Symbol(options.char, options.symbolfont)); + } +}