Merge pull request #1848 from dolanmiu/feat/refactoring
Different Language support
This commit is contained in:
69
demo/83-setting-languages.ts
Normal file
69
demo/83-setting-languages.ts
Normal file
@ -0,0 +1,69 @@
|
||||
// Simple example to add text to a document
|
||||
// Import from 'docx' rather than '../build' if you install from npm
|
||||
import * as fs from "fs";
|
||||
import { Document, Packer, Paragraph } from "../build";
|
||||
|
||||
const doc = new Document({
|
||||
styles: {
|
||||
default: {
|
||||
document: {
|
||||
run: {
|
||||
color: "ff0000",
|
||||
language: {
|
||||
value: "es-ES",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
paragraphStyles: [
|
||||
{
|
||||
id: "frenchNormal",
|
||||
name: "French Normal",
|
||||
basedOn: "Normal",
|
||||
next: "Normal",
|
||||
run: {
|
||||
color: "999999",
|
||||
italics: true,
|
||||
language: {
|
||||
value: "fr-FR",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "koreanNormal",
|
||||
name: "Korean Normal",
|
||||
basedOn: "Normal",
|
||||
next: "Normal",
|
||||
run: {
|
||||
color: "0000ff",
|
||||
bold: true,
|
||||
language: {
|
||||
value: "ko-KR",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
sections: [
|
||||
{
|
||||
properties: {},
|
||||
children: [
|
||||
new Paragraph({
|
||||
text: "Yo vivo en Granada, una ciudad pequeña que tiene monumentos muy importantes como la Alhambra. Aquí la comida es deliciosa y son famosos el gazpacho, el rebujito y el salmorejo.",
|
||||
}),
|
||||
new Paragraph({
|
||||
text: "Toute personne a droit à l'éducation. L'éducation doit être gratuite, au moins en ce qui concerne l'enseignement élémentaire et fondamental. L'enseignement élémentaire est obligatoire. L'enseignement technique et professionnel doit être généralisé; l'accès aux études supérieures doit être ouvert en pleine égalité à tous en fonction de leur mérite.",
|
||||
style: "frenchNormal",
|
||||
}),
|
||||
new Paragraph({
|
||||
text: "대법관은 대법원장의 제청으로 국회의 동의를 얻어 대통령이 임명한다. 강화조약. 국가는 국민 모두의 생산 및 생활의 기반이 되는 국토의 효율적이고 균형있는 이용·개발과 보전을 위하여 법률이 정하는 바에 의하여 그에 관한 필요한 제한과 의무를 과할 수 있다, 국가는 청원에 대하여 심사할 의무를 진다.",
|
||||
style: "koreanNormal",
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
Packer.toBuffer(doc).then((buffer) => {
|
||||
fs.writeFileSync("My Document.docx", buffer);
|
||||
});
|
29
src/file/paragraph/run/language.spec.ts
Normal file
29
src/file/paragraph/run/language.spec.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { expect } from "chai";
|
||||
|
||||
import { Formatter } from "@export/formatter";
|
||||
|
||||
import { createLanguageComponent } from "./language";
|
||||
|
||||
describe("Language", () => {
|
||||
describe("#createLanguageComponent", () => {
|
||||
it("should create a language component", () => {
|
||||
const tree = new Formatter().format(
|
||||
createLanguageComponent({
|
||||
value: "en-US",
|
||||
eastAsia: "zh-CN",
|
||||
bidirectional: "ar-SA",
|
||||
}),
|
||||
);
|
||||
|
||||
expect(tree).to.deep.equal({
|
||||
"w:lang": {
|
||||
_attr: {
|
||||
"w:bidi": "ar-SA",
|
||||
"w:eastAsia": "zh-CN",
|
||||
"w:val": "en-US",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
35
src/file/paragraph/run/language.ts
Normal file
35
src/file/paragraph/run/language.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { BuilderElement, XmlComponent } from "@file/xml-components";
|
||||
|
||||
// <xsd:complexType name="CT_Language">
|
||||
// <xsd:attribute name="val" type="s:ST_Lang" use="optional"/>
|
||||
// <xsd:attribute name="eastAsia" type="s:ST_Lang" use="optional"/>
|
||||
// <xsd:attribute name="bidi" type="s:ST_Lang" use="optional"/>
|
||||
// </xsd:complexType>
|
||||
export interface ILanguageOptions {
|
||||
readonly value?: string;
|
||||
readonly eastAsia?: string;
|
||||
readonly bidirectional?: string;
|
||||
}
|
||||
|
||||
export const createLanguageComponent = (options: ILanguageOptions): XmlComponent =>
|
||||
new BuilderElement<{
|
||||
readonly value?: string;
|
||||
readonly eastAsia?: string;
|
||||
readonly bidirectional?: string;
|
||||
}>({
|
||||
name: "w:lang",
|
||||
attributes: {
|
||||
value: {
|
||||
key: "w:val",
|
||||
value: options.value,
|
||||
},
|
||||
eastAsia: {
|
||||
key: "w:eastAsia",
|
||||
value: options.eastAsia,
|
||||
},
|
||||
bidirectional: {
|
||||
key: "w:bidi",
|
||||
value: options.bidirectional,
|
||||
},
|
||||
},
|
||||
});
|
@ -12,6 +12,7 @@ import {
|
||||
|
||||
import { EmphasisMark, EmphasisMarkType } from "./emphasis-mark";
|
||||
import { CharacterSpacing, Color, Highlight, HighlightComplexScript } from "./formatting";
|
||||
import { createLanguageComponent, ILanguageOptions } from "./language";
|
||||
import { IFontAttributesProperties, RunFonts } from "./run-fonts";
|
||||
import { SubScript, SuperScript } from "./script";
|
||||
import { Underline, UnderlineType } from "./underline";
|
||||
@ -51,6 +52,7 @@ export interface IRunStylePropertiesOptions {
|
||||
readonly emboss?: boolean;
|
||||
readonly imprint?: boolean;
|
||||
readonly revision?: IRunPropertiesChangeOptions;
|
||||
readonly language?: ILanguageOptions;
|
||||
readonly border?: IBorderOptions;
|
||||
readonly vanish?: boolean;
|
||||
readonly specVanish?: boolean;
|
||||
@ -240,6 +242,10 @@ export class RunProperties extends IgnoreIfEmptyXmlComponent {
|
||||
if (options.scale !== undefined) {
|
||||
this.push(new NumberValueElement("w:w", options.scale));
|
||||
}
|
||||
|
||||
if (options.language) {
|
||||
this.push(createLanguageComponent(options.language));
|
||||
}
|
||||
}
|
||||
|
||||
public push(item: XmlComponent): void {
|
||||
|
@ -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",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -12,13 +12,9 @@ export class Relationships extends XmlComponent {
|
||||
);
|
||||
}
|
||||
|
||||
public addRelationship(relationship: Relationship): void {
|
||||
this.root.push(relationship);
|
||||
}
|
||||
|
||||
public createRelationship(id: number | string, type: RelationshipType, target: string, targetMode?: TargetModeType): Relationship {
|
||||
const relationship = new Relationship(`rId${id}`, type, target, targetMode);
|
||||
this.addRelationship(relationship);
|
||||
this.root.push(relationship);
|
||||
|
||||
return relationship;
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { BaseXmlComponent, IContext } from "./base";
|
||||
import { IXmlableObject } from "./xmlable-object";
|
||||
import { IXmlableObject, IXmlAttribute } from "./xmlable-object";
|
||||
|
||||
export type AttributeMap<T> = { readonly [P in keyof T]: string };
|
||||
|
||||
export type AttributeData = { readonly [key: string]: boolean | number | string };
|
||||
export type AttributePayload<T> = { readonly [P in keyof T]: { readonly key: string; readonly value: T[P] } };
|
||||
|
||||
export abstract class XmlAttributeComponent<T extends object> extends BaseXmlComponent {
|
||||
// tslint:disable-next-line:readonly-keyword
|
||||
protected readonly root: T;
|
||||
protected readonly xmlKeys?: AttributeMap<T>;
|
||||
|
||||
public constructor(properties: T) {
|
||||
public constructor(private readonly root: T) {
|
||||
super("_attr");
|
||||
this.root = properties;
|
||||
}
|
||||
|
||||
public prepForXml(_: IContext): IXmlableObject {
|
||||
@ -26,3 +26,16 @@ export abstract class XmlAttributeComponent<T extends object> extends BaseXmlCom
|
||||
return { _attr: attrs };
|
||||
}
|
||||
}
|
||||
|
||||
export class NextAttributeComponent<T extends AttributeData> extends BaseXmlComponent {
|
||||
public constructor(private readonly root: AttributePayload<T>) {
|
||||
super("_attr");
|
||||
}
|
||||
|
||||
public prepForXml(_: IContext): IXmlableObject {
|
||||
const attrs = Object.values<{ readonly key: string; readonly value: string | boolean | number }>(this.root)
|
||||
.filter(({ value }) => !!value)
|
||||
.reduce((acc, { key, value }) => ({ ...acc, [key]: value }), {} as IXmlAttribute);
|
||||
return { _attr: attrs };
|
||||
}
|
||||
}
|
||||
|
@ -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,17 @@ export class StringContainer extends XmlComponent {
|
||||
this.root.push(val);
|
||||
}
|
||||
}
|
||||
|
||||
export class BuilderElement<T extends AttributeData> extends XmlComponent {
|
||||
public constructor(options: {
|
||||
readonly name: string;
|
||||
readonly attributes?: AttributePayload<T>;
|
||||
readonly children?: readonly XmlComponent[];
|
||||
}) {
|
||||
super(options.name);
|
||||
|
||||
if (options.attributes) {
|
||||
this.root.push(new NextAttributeComponent(options.attributes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user