diff --git a/src/file/paragraph/run/properties.ts b/src/file/paragraph/run/properties.ts index 19593b62dd..f5ddc4750b 100644 --- a/src/file/paragraph/run/properties.ts +++ b/src/file/paragraph/run/properties.ts @@ -2,6 +2,7 @@ import { BorderElement, IBorderOptions } from "@file/border"; import { IShadingAttributesProperties, Shading } from "@file/shading"; import { ChangeAttributes, IChangedAttributesProperties } from "@file/track-revision/track-revision"; import { + BuilderElement, HpsMeasureElement, IgnoreIfEmptyXmlComponent, NumberValueElement, @@ -51,6 +52,16 @@ export interface IRunStylePropertiesOptions { readonly emboss?: boolean; readonly imprint?: boolean; readonly revision?: IRunPropertiesChangeOptions; + // + // + // + // + // + readonly language?: { + readonly value?: string; + readonly eastAsia?: string; + readonly bidirectional?: string; + }; readonly border?: IBorderOptions; readonly vanish?: boolean; readonly specVanish?: boolean; @@ -240,6 +251,32 @@ export class RunProperties extends IgnoreIfEmptyXmlComponent { if (options.scale !== undefined) { this.push(new NumberValueElement("w:w", options.scale)); } + + if (options.language) { + this.push( + new BuilderElement<{ + readonly value: string; + readonly eastAsia: string; + readonly bidirectional: string; + }>({ + name: "w:lang", + attributes: { + value: { + key: "w:val", + value: options.language.value, + }, + eastAsia: { + key: "w:eastAsia", + value: options.language.eastAsia, + }, + bidirectional: { + key: "w:bidi", + value: options.language.bidirectional, + }, + }, + }), + ); + } } public push(item: XmlComponent): void { diff --git a/src/file/paragraph/run/run.spec.ts b/src/file/paragraph/run/run.spec.ts index b178536435..2a207a7b87 100644 --- a/src/file/paragraph/run/run.spec.ts +++ b/src/file/paragraph/run/run.spec.ts @@ -580,5 +580,35 @@ describe("Run", () => { }); }); }); + + describe("#language", () => { + it("should correctly set the language", () => { + const run = new Run({ + language: { + value: "en-US", + eastAsia: "zh-CN", + bidirectional: "ar-SA", + }, + }); + const tree = new Formatter().format(run); + expect(tree).to.deep.equal({ + "w:r": [ + { + "w:rPr": [ + { + "w:lang": { + _attr: { + "w:val": "en-US", + "w:eastAsia": "zh-CN", + "w:bidi": "ar-SA", + }, + }, + }, + ], + }, + ], + }); + }); + }); }); }); diff --git a/src/file/xml-components/default-attributes.ts b/src/file/xml-components/default-attributes.ts index bf84e95eef..c3fb1b8105 100644 --- a/src/file/xml-components/default-attributes.ts +++ b/src/file/xml-components/default-attributes.ts @@ -3,14 +3,20 @@ import { IXmlableObject } from "./xmlable-object"; export type AttributeMap = { readonly [P in keyof T]: string }; +export type AttributeData = { readonly [key: string]: boolean | number | string }; +export type AttributePayload = Record< + keyof T, + { + readonly key: string; + readonly value?: T[keyof T]; + } +>; + export abstract class XmlAttributeComponent extends BaseXmlComponent { - // tslint:disable-next-line:readonly-keyword - protected readonly root: T; protected readonly xmlKeys?: AttributeMap; - public constructor(properties: T) { + public constructor(private readonly root: T) { super("_attr"); - this.root = properties; } public prepForXml(_: IContext): IXmlableObject { @@ -26,3 +32,20 @@ export abstract class XmlAttributeComponent extends BaseXmlCom return { _attr: attrs }; } } + +export class NextAttributeComponent extends BaseXmlComponent { + public constructor(private readonly root: AttributePayload) { + super("_attr"); + } + + public prepForXml(_: IContext): IXmlableObject { + const attrs = {}; + Object.values(this.root).forEach(({ key, value }) => { + if (value !== undefined) { + // eslint-disable-next-line functional/immutable-data + attrs[key] = value; + } + }); + return { _attr: attrs }; + } +} diff --git a/src/file/xml-components/simple-elements.ts b/src/file/xml-components/simple-elements.ts index aca3355c28..b049244746 100644 --- a/src/file/xml-components/simple-elements.ts +++ b/src/file/xml-components/simple-elements.ts @@ -1,4 +1,4 @@ -import { Attributes, XmlComponent } from "@file/xml-components"; +import { AttributeData, AttributePayload, Attributes, NextAttributeComponent, XmlComponent } from "@file/xml-components"; import { hpsMeasureValue } from "@util/values"; @@ -70,3 +70,13 @@ export class StringContainer extends XmlComponent { this.root.push(val); } } + +export class BuilderElement extends XmlComponent { + public constructor(options: { readonly attributes?: AttributePayload; readonly name: string }) { + super(options.name); + + if (options.attributes) { + this.root.push(new NextAttributeComponent(options.attributes)); + } + } +}