Merge pull request #1848 from dolanmiu/feat/refactoring

Different Language support
This commit is contained in:
Dolan
2022-12-21 04:38:38 +00:00
committed by GitHub
8 changed files with 203 additions and 11 deletions

View 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);
});

View 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",
},
},
});
});
});
});

View 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,
},
},
});

View File

@ -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 {

View File

@ -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",
},
},
},
],
},
],
});
});
});
});
});

View File

@ -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;
}

View File

@ -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 };
}
}

View File

@ -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));
}
}
}