Add language support

This commit is contained in:
Dolan
2022-12-20 21:42:26 +00:00
parent 03d5152e7c
commit 3282f762df
6 changed files with 146 additions and 50 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

@ -2,7 +2,6 @@ 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,
@ -13,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";
@ -52,16 +52,7 @@ export interface IRunStylePropertiesOptions {
readonly emboss?: boolean;
readonly imprint?: boolean;
readonly revision?: IRunPropertiesChangeOptions;
// <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>
readonly language?: {
readonly value?: string;
readonly eastAsia?: string;
readonly bidirectional?: string;
};
readonly language?: ILanguageOptions;
readonly border?: IBorderOptions;
readonly vanish?: boolean;
readonly specVanish?: boolean;
@ -253,29 +244,7 @@ export class RunProperties extends IgnoreIfEmptyXmlComponent {
}
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,
},
},
}),
);
this.push(createLanguageComponent(options.language));
}
}

View File

@ -1,16 +1,10 @@
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 extends AttributeData> = Record<
keyof T,
{
readonly key: string;
readonly value?: T[keyof T];
}
>;
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 {
protected readonly xmlKeys?: AttributeMap<T>;
@ -39,13 +33,9 @@ export class NextAttributeComponent<T extends AttributeData> extends BaseXmlComp
}
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;
}
});
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

@ -72,7 +72,11 @@ export class StringContainer extends XmlComponent {
}
export class BuilderElement<T extends AttributeData> extends XmlComponent {
public constructor(options: { readonly attributes?: AttributePayload<T>; readonly name: string }) {
public constructor(options: {
readonly name: string;
readonly attributes?: AttributePayload<T>;
readonly children?: readonly XmlComponent[];
}) {
super(options.name);
if (options.attributes) {