Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
a708475539 | |||
9c60cfcbc7 | |||
a5454edc61 | |||
7152abfe48 | |||
075eeb7e3c | |||
f0acb3f3fb | |||
ae048833a1 | |||
ebae72004c | |||
13744abff5 | |||
492ef29d7f | |||
a6e6463a36 | |||
2ef596543b | |||
85005c3c07 | |||
0d2b433446 | |||
b1f67652e9 | |||
4e2befb7ef | |||
a887927968 | |||
ee6db1429a | |||
170309a7ed | |||
05fcf6edd4 |
@ -14,6 +14,7 @@
|
|||||||
[![Known Vulnerabilities][snky-image]][snky-url]
|
[![Known Vulnerabilities][snky-image]][snky-url]
|
||||||
[![PRs Welcome][pr-image]][pr-url]
|
[![PRs Welcome][pr-image]][pr-url]
|
||||||
[![codecov][codecov-image]][codecov-url]
|
[![codecov][codecov-image]][codecov-url]
|
||||||
|
[![Docx.js Editor][docxjs-editor-image]][docxjs-editor-url]
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://i.imgur.com/QeL1HuU.png" alt="drawing"/>
|
<img src="https://i.imgur.com/QeL1HuU.png" alt="drawing"/>
|
||||||
@ -64,6 +65,10 @@ More [here](https://github.com/dolanmiu/docx/tree/master/demo)
|
|||||||
|
|
||||||
Please refer to the [documentation at https://docx.js.org/](https://docx.js.org/) for details on how to use this library, examples and much more!
|
Please refer to the [documentation at https://docx.js.org/](https://docx.js.org/) for details on how to use this library, examples and much more!
|
||||||
|
|
||||||
|
# Playground
|
||||||
|
|
||||||
|
Experience `docx` in action through [Docx.js Editor][docxjs-editor-url], an interactive playground where you can code and preview the results in real-time.
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
Check the [demo folder](https://github.com/dolanmiu/docx/tree/master/demo) for examples.
|
Check the [demo folder](https://github.com/dolanmiu/docx/tree/master/demo) for examples.
|
||||||
@ -115,3 +120,5 @@ Made with 💖
|
|||||||
[patreon-url]: https://www.patreon.com/dolanmiu
|
[patreon-url]: https://www.patreon.com/dolanmiu
|
||||||
[browserstack-image]: https://user-images.githubusercontent.com/2917613/54233552-128e9d00-4505-11e9-88fb-025a4e04007c.png
|
[browserstack-image]: https://user-images.githubusercontent.com/2917613/54233552-128e9d00-4505-11e9-88fb-025a4e04007c.png
|
||||||
[browserstack-url]: https://www.browserstack.com
|
[browserstack-url]: https://www.browserstack.com
|
||||||
|
[docxjs-editor-image]: https://img.shields.io/badge/Docx.js%20Editor-2b579a.svg?style=flat&logo=javascript&logoColor=white
|
||||||
|
[docxjs-editor-url]: https://docxjs-editor.vercel.app/
|
||||||
|
60
demo/95-paragraph-style-with-shading-and-borders.ts
Normal file
60
demo/95-paragraph-style-with-shading-and-borders.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import { BorderStyle, Document, Packer, Paragraph, TextRun } from "docx";
|
||||||
|
|
||||||
|
const doc = new Document({
|
||||||
|
styles: {
|
||||||
|
paragraphStyles: [
|
||||||
|
{
|
||||||
|
id: "withSingleBlackBordersAndYellowShading",
|
||||||
|
name: "Paragraph Style with Black Borders and Yellow Shading",
|
||||||
|
basedOn: "Normal",
|
||||||
|
paragraph: {
|
||||||
|
shading: {
|
||||||
|
color: "#fff000",
|
||||||
|
type: "solid",
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
top: {
|
||||||
|
style: BorderStyle.SINGLE,
|
||||||
|
color: "#000000",
|
||||||
|
size: 4,
|
||||||
|
},
|
||||||
|
bottom: {
|
||||||
|
style: BorderStyle.SINGLE,
|
||||||
|
color: "#000000",
|
||||||
|
size: 4,
|
||||||
|
},
|
||||||
|
left: {
|
||||||
|
style: BorderStyle.SINGLE,
|
||||||
|
color: "#000000",
|
||||||
|
size: 4,
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
style: BorderStyle.SINGLE,
|
||||||
|
color: "#000000",
|
||||||
|
size: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
children: [
|
||||||
|
new Paragraph({
|
||||||
|
style: "withSingleBlackBordersAndYellowShading",
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Packer.toBuffer(doc).then((buffer) => {
|
||||||
|
fs.writeFileSync("My Document.docx", buffer);
|
||||||
|
});
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> Packers are the way in which `docx` turns your code into `.docx` format. It is completely decoupled from the `docx.Document`.
|
> Packers are the way in which `docx` turns your code into `.docx` format. It is completely decoupled from the `docx.Document`.
|
||||||
|
|
||||||
Packers works in both a node and browser environment (Angular etc). Now, the packer returns a `Buffer`, `Blob` or `base64 string`. It is up to you to take that and persist it with node's `fs`, send it down as a downloadable file, or anything else you wish. As of `version 4+`, this library will not have options to export to PDF.
|
Packers works in both a node and browser environment (Angular etc). Now, the packer returns a `Buffer`, `Blob`, `string`, `base64 string`, `ArrayBuffer`, or `Stream`. It is up to you to take that and persist it with node's `fs`, send it down as a downloadable file, or anything else you wish. As of `version 4+`, this library will not have options to export to PDF.
|
||||||
|
|
||||||
### Export as Buffer
|
### Export as Buffer
|
||||||
|
|
||||||
@ -14,6 +14,14 @@ Packer.toBuffer(doc).then((buffer) => {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Export as string
|
||||||
|
|
||||||
|
```ts
|
||||||
|
Packer.toString(doc).then((string) => {
|
||||||
|
console.log(string);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### Export as a `base64` string
|
### Export as a `base64` string
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
@ -32,3 +40,46 @@ Packer.toBlob(doc).then((blob) => {
|
|||||||
saveAs(blob, "example.docx");
|
saveAs(blob, "example.docx");
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Export as ArrayBuffer
|
||||||
|
|
||||||
|
This may be useful when working in a Node.js worker.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
Packer.toArrayBuffer(doc).then((arrayBuffer) => {
|
||||||
|
port.postMessage(arrayBuffer, [arrayBuffer]);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export as a Stream
|
||||||
|
|
||||||
|
```ts
|
||||||
|
Packer.toStream(doc).then((stream) => {
|
||||||
|
// read from stream
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export using optional arguments
|
||||||
|
|
||||||
|
The `Packer` methods support 2 optional arguments.
|
||||||
|
|
||||||
|
The first is for controlling the indentation of the xml and should be a `boolean` or `keyof typeof PrettifyType`.
|
||||||
|
|
||||||
|
The second is an array of subfile overrides (`{path: string, data: string}[]`). These overrides can be used to write additional subfiles to the result or even override default subfiles in the case that the default handling of these subfiles does not meet your needs.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const overrides = [{ path: "word/commentsExtended.xml", data: "string_data" }];
|
||||||
|
Packer.toString(doc, true, overrides).then((string) => {
|
||||||
|
console.log(string);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Export to arbitrary formats
|
||||||
|
|
||||||
|
You can also use the lower-level `Packer.pack` method to export to any specified type.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
Packer.pack(doc, 'string').then((string) => {
|
||||||
|
console.log(string);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
7188
package-lock.json
generated
7188
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "docx",
|
"name": "docx",
|
||||||
"version": "9.1.1",
|
"version": "9.2.0",
|
||||||
"description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.",
|
"description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.umd.cjs",
|
"main": "dist/index.umd.cjs",
|
||||||
@ -61,7 +61,7 @@
|
|||||||
"@types/node": "^22.7.5",
|
"@types/node": "^22.7.5",
|
||||||
"hash.js": "^1.1.7",
|
"hash.js": "^1.1.7",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"nanoid": "^5.0.4",
|
"nanoid": "^5.1.3",
|
||||||
"xml": "^1.0.1",
|
"xml": "^1.0.1",
|
||||||
"xml-js": "^1.6.8"
|
"xml-js": "^1.6.8"
|
||||||
},
|
},
|
||||||
@ -79,8 +79,8 @@
|
|||||||
"@types/xml": "^1.0.8",
|
"@types/xml": "^1.0.8",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.8.1",
|
"@typescript-eslint/eslint-plugin": "^8.8.1",
|
||||||
"@typescript-eslint/parser": "^8.8.1",
|
"@typescript-eslint/parser": "^8.8.1",
|
||||||
"@vitest/coverage-v8": "^2.1.8",
|
"@vitest/coverage-v8": "^3.0.8",
|
||||||
"@vitest/ui": "^2.1.2",
|
"@vitest/ui": "^3.0.8",
|
||||||
"cspell": "^8.2.3",
|
"cspell": "^8.2.3",
|
||||||
"docsify-cli": "^4.3.0",
|
"docsify-cli": "^4.3.0",
|
||||||
"eslint": "^9.13.0",
|
"eslint": "^9.13.0",
|
||||||
@ -95,7 +95,7 @@
|
|||||||
"glob": "^11.0.0",
|
"glob": "^11.0.0",
|
||||||
"inquirer": "^12.0.0",
|
"inquirer": "^12.0.0",
|
||||||
"jiti": "^2.3.3",
|
"jiti": "^2.3.3",
|
||||||
"jsdom": "^25.0.1",
|
"jsdom": "^26.0.0",
|
||||||
"pre-commit": "^1.2.2",
|
"pre-commit": "^1.2.2",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
"tsconfig-paths": "^4.0.0",
|
"tsconfig-paths": "^4.0.0",
|
||||||
@ -106,9 +106,9 @@
|
|||||||
"unzipper": "^0.12.3",
|
"unzipper": "^0.12.3",
|
||||||
"vite": "^6.0.1",
|
"vite": "^6.0.1",
|
||||||
"vite-plugin-dts": "^4.2.4",
|
"vite-plugin-dts": "^4.2.4",
|
||||||
"vite-plugin-node-polyfills": "^0.22.0",
|
"vite-plugin-node-polyfills": "^0.23.0",
|
||||||
"vite-tsconfig-paths": "^5.0.1",
|
"vite-tsconfig-paths": "^5.0.1",
|
||||||
"vitest": "^2.1.8"
|
"vitest": "^3.0.8"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
|
@ -112,6 +112,41 @@ describe("Compiler", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
it(
|
||||||
|
"should pack subfile overrides",
|
||||||
|
async () => {
|
||||||
|
const file = new File({
|
||||||
|
sections: [],
|
||||||
|
comments: {
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const subfileData1 = "comments";
|
||||||
|
const subfileData2 = "commentsExtended";
|
||||||
|
const overrides = [
|
||||||
|
{ path: "word/comments.xml", data: subfileData1 },
|
||||||
|
{ path: "word/commentsExtended.xml", data: subfileData2 },
|
||||||
|
];
|
||||||
|
const zipFile = compiler.compile(file, "", overrides);
|
||||||
|
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||||
|
|
||||||
|
expect(fileNames).is.an.instanceof(Array);
|
||||||
|
expect(fileNames).has.length(20);
|
||||||
|
|
||||||
|
expect(fileNames).to.include("word/comments.xml");
|
||||||
|
expect(fileNames).to.include("word/commentsExtended.xml");
|
||||||
|
|
||||||
|
const commentsText = await zipFile.file("word/comments.xml")?.async("text");
|
||||||
|
const commentsExtendedText = await zipFile.file("word/commentsExtended.xml")?.async("text");
|
||||||
|
|
||||||
|
expect(commentsText).toBe(subfileData1);
|
||||||
|
expect(commentsExtendedText).toBe(subfileData2);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timeout: 99999999,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
it("should call the format method X times equalling X files to be formatted", () => {
|
it("should call the format method X times equalling X files to be formatted", () => {
|
||||||
// This test is required because before, there was a case where Document was formatted twice, which was inefficient
|
// This test is required because before, there was a case where Document was formatted twice, which was inefficient
|
||||||
// This also caused issues such as running prepForXml multiple times as format() was ran multiple times.
|
// This also caused issues such as running prepForXml multiple times as format() was ran multiple times.
|
||||||
|
@ -9,7 +9,7 @@ import { ImageReplacer } from "./image-replacer";
|
|||||||
import { NumberingReplacer } from "./numbering-replacer";
|
import { NumberingReplacer } from "./numbering-replacer";
|
||||||
import { PrettifyType } from "./packer";
|
import { PrettifyType } from "./packer";
|
||||||
|
|
||||||
type IXmlifyedFile = {
|
export type IXmlifyedFile = {
|
||||||
readonly data: string;
|
readonly data: string;
|
||||||
readonly path: string;
|
readonly path: string;
|
||||||
};
|
};
|
||||||
@ -47,7 +47,11 @@ export class Compiler {
|
|||||||
this.numberingReplacer = new NumberingReplacer();
|
this.numberingReplacer = new NumberingReplacer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public compile(file: File, prettifyXml?: (typeof PrettifyType)[keyof typeof PrettifyType]): JSZip {
|
public compile(
|
||||||
|
file: File,
|
||||||
|
prettifyXml?: (typeof PrettifyType)[keyof typeof PrettifyType],
|
||||||
|
overrides: readonly IXmlifyedFile[] = [],
|
||||||
|
): JSZip {
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
const xmlifiedFileMapping = this.xmlifyFile(file, prettifyXml);
|
const xmlifiedFileMapping = this.xmlifyFile(file, prettifyXml);
|
||||||
const map = new Map<string, IXmlifyedFile | readonly IXmlifyedFile[]>(Object.entries(xmlifiedFileMapping));
|
const map = new Map<string, IXmlifyedFile | readonly IXmlifyedFile[]>(Object.entries(xmlifiedFileMapping));
|
||||||
@ -62,6 +66,10 @@ export class Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const subFile of overrides) {
|
||||||
|
zip.file(subFile.path, subFile.data);
|
||||||
|
}
|
||||||
|
|
||||||
for (const data of file.Media.Array) {
|
for (const data of file.Media.Array) {
|
||||||
if (data.type !== "svg") {
|
if (data.type !== "svg") {
|
||||||
zip.file(`word/media/${data.fileName}`, data.data);
|
zip.file(`word/media/${data.fileName}`, data.data);
|
||||||
|
@ -46,7 +46,7 @@ describe("Packer", () => {
|
|||||||
|
|
||||||
await Packer.toString(file, true);
|
await Packer.toString(file, true);
|
||||||
|
|
||||||
expect(spy).toBeCalledWith(expect.anything(), PrettifyType.WITH_2_BLANKS);
|
expect(spy).toBeCalledWith(expect.anything(), PrettifyType.WITH_2_BLANKS, expect.anything());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should use a prettify value", async () => {
|
it("should use a prettify value", async () => {
|
||||||
@ -55,7 +55,7 @@ describe("Packer", () => {
|
|||||||
|
|
||||||
await Packer.toString(file, PrettifyType.WITH_4_BLANKS);
|
await Packer.toString(file, PrettifyType.WITH_4_BLANKS);
|
||||||
|
|
||||||
expect(spy).toBeCalledWith(expect.anything(), PrettifyType.WITH_4_BLANKS);
|
expect(spy).toBeCalledWith(expect.anything(), PrettifyType.WITH_4_BLANKS, expect.anything());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should use an undefined prettify value", async () => {
|
it("should use an undefined prettify value", async () => {
|
||||||
@ -64,7 +64,32 @@ describe("Packer", () => {
|
|||||||
|
|
||||||
await Packer.toString(file, false);
|
await Packer.toString(file, false);
|
||||||
|
|
||||||
expect(spy).toBeCalledWith(expect.anything(), undefined);
|
expect(spy).toBeCalledWith(expect.anything(), undefined, expect.anything());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("overrides", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use an overrides value", async () => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const spy = vi.spyOn((Packer as any).compiler, "compile");
|
||||||
|
const overrides = [{ path: "word/comments.xml", data: "comments" }];
|
||||||
|
|
||||||
|
await Packer.toString(file, true, overrides);
|
||||||
|
|
||||||
|
expect(spy).toBeCalledWith(expect.anything(), expect.anything(), overrides);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use a default overrides value", async () => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const spy = vi.spyOn((Packer as any).compiler, "compile");
|
||||||
|
|
||||||
|
await Packer.toString(file);
|
||||||
|
|
||||||
|
expect(spy).toBeCalledWith(expect.anything(), undefined, []);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -162,6 +187,33 @@ describe("Packer", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("#toArrayBuffer()", () => {
|
||||||
|
it("should create a standard docx file", async () => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
vi.spyOn((Packer as any).compiler, "compile").mockReturnValue({
|
||||||
|
generateAsync: () => vi.fn(),
|
||||||
|
});
|
||||||
|
const str = await Packer.toArrayBuffer(file);
|
||||||
|
|
||||||
|
assert.isDefined(str);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle exception if it throws any", () => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
vi.spyOn((Packer as any).compiler, "compile").mockImplementation(() => {
|
||||||
|
throw new Error();
|
||||||
|
});
|
||||||
|
|
||||||
|
return Packer.toArrayBuffer(file).catch((error) => {
|
||||||
|
assert.isDefined(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.resetAllMocks();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("#toStream()", () => {
|
describe("#toStream()", () => {
|
||||||
it("should create a standard docx file", async () => {
|
it("should create a standard docx file", async () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { Stream } from "stream";
|
import { Stream } from "stream";
|
||||||
|
|
||||||
import { File } from "@file/file";
|
import { File } from "@file/file";
|
||||||
|
import { OutputByType, OutputType } from "@util/output-type";
|
||||||
|
|
||||||
import { Compiler } from "./next-compiler";
|
import { Compiler, IXmlifyedFile } from "./next-compiler";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use blanks to prettify
|
* Use blanks to prettify
|
||||||
@ -21,53 +22,68 @@ const convertPrettifyType = (
|
|||||||
prettify === true ? PrettifyType.WITH_2_BLANKS : prettify === false ? undefined : prettify;
|
prettify === true ? PrettifyType.WITH_2_BLANKS : prettify === false ? undefined : prettify;
|
||||||
|
|
||||||
export class Packer {
|
export class Packer {
|
||||||
public static async toString(file: File, prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType]): Promise<string> {
|
// eslint-disable-next-line require-await
|
||||||
const zip = this.compiler.compile(file, convertPrettifyType(prettify));
|
public static async pack<T extends OutputType>(
|
||||||
const zipData = await zip.generateAsync({
|
file: File,
|
||||||
type: "string",
|
type: T,
|
||||||
|
prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType],
|
||||||
|
overrides: readonly IXmlifyedFile[] = [],
|
||||||
|
): Promise<OutputByType[T]> {
|
||||||
|
const zip = this.compiler.compile(file, convertPrettifyType(prettify), overrides);
|
||||||
|
return zip.generateAsync({
|
||||||
|
type,
|
||||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
compression: "DEFLATE",
|
compression: "DEFLATE",
|
||||||
});
|
});
|
||||||
|
|
||||||
return zipData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async toBuffer(file: File, prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType]): Promise<Buffer> {
|
public static toString(
|
||||||
const zip = this.compiler.compile(file, convertPrettifyType(prettify));
|
file: File,
|
||||||
const zipData = await zip.generateAsync({
|
prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType],
|
||||||
type: "nodebuffer",
|
overrides: readonly IXmlifyedFile[] = [],
|
||||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
): Promise<string> {
|
||||||
compression: "DEFLATE",
|
return Packer.pack(file, "string", prettify, overrides);
|
||||||
});
|
|
||||||
|
|
||||||
return zipData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async toBase64String(file: File, prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType]): Promise<string> {
|
public static toBuffer(
|
||||||
const zip = this.compiler.compile(file, convertPrettifyType(prettify));
|
file: File,
|
||||||
const zipData = await zip.generateAsync({
|
prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType],
|
||||||
type: "base64",
|
overrides: readonly IXmlifyedFile[] = [],
|
||||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
): Promise<Buffer> {
|
||||||
compression: "DEFLATE",
|
return Packer.pack(file, "nodebuffer", prettify, overrides);
|
||||||
});
|
|
||||||
|
|
||||||
return zipData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async toBlob(file: File, prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType]): Promise<Blob> {
|
public static toBase64String(
|
||||||
const zip = this.compiler.compile(file, convertPrettifyType(prettify));
|
file: File,
|
||||||
const zipData = await zip.generateAsync({
|
prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType],
|
||||||
type: "blob",
|
overrides: readonly IXmlifyedFile[] = [],
|
||||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
): Promise<string> {
|
||||||
compression: "DEFLATE",
|
return Packer.pack(file, "base64", prettify, overrides);
|
||||||
});
|
|
||||||
|
|
||||||
return zipData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static toStream(file: File, prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType]): Stream {
|
public static toBlob(
|
||||||
|
file: File,
|
||||||
|
prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType],
|
||||||
|
overrides: readonly IXmlifyedFile[] = [],
|
||||||
|
): Promise<Blob> {
|
||||||
|
return Packer.pack(file, "blob", prettify, overrides);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static toArrayBuffer(
|
||||||
|
file: File,
|
||||||
|
prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType],
|
||||||
|
overrides: readonly IXmlifyedFile[] = [],
|
||||||
|
): Promise<ArrayBuffer> {
|
||||||
|
return Packer.pack(file, "arraybuffer", prettify, overrides);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static toStream(
|
||||||
|
file: File,
|
||||||
|
prettify?: boolean | (typeof PrettifyType)[keyof typeof PrettifyType],
|
||||||
|
overrides: readonly IXmlifyedFile[] = [],
|
||||||
|
): Stream {
|
||||||
const stream = new Stream();
|
const stream = new Stream();
|
||||||
const zip = this.compiler.compile(file, convertPrettifyType(prettify));
|
const zip = this.compiler.compile(file, convertPrettifyType(prettify), overrides);
|
||||||
|
|
||||||
zip.generateAsync({
|
zip.generateAsync({
|
||||||
type: "nodebuffer",
|
type: "nodebuffer",
|
||||||
|
@ -21,3 +21,4 @@ export * from "./vertical-align";
|
|||||||
export * from "./checkbox";
|
export * from "./checkbox";
|
||||||
export * from "./fonts";
|
export * from "./fonts";
|
||||||
export * from "./textbox";
|
export * from "./textbox";
|
||||||
|
export { type IPropertiesOptions } from "./core-properties";
|
||||||
|
@ -24,7 +24,7 @@ class SpacingAttributes extends XmlAttributeComponent<ISpacingProperties> {
|
|||||||
line: "w:line",
|
line: "w:line",
|
||||||
lineRule: "w:lineRule",
|
lineRule: "w:lineRule",
|
||||||
beforeAutoSpacing: "w:beforeAutospacing",
|
beforeAutoSpacing: "w:beforeAutospacing",
|
||||||
afterAutoSpacing: "w:afterAutoSpacing",
|
afterAutoSpacing: "w:afterAutospacing",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
src/file/paragraph/math/bar/index.ts
Normal file
2
src/file/paragraph/math/bar/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./math-bar";
|
||||||
|
export * from "./math-bar-properties";
|
11
src/file/paragraph/math/bar/math-bar-pos.ts
Normal file
11
src/file/paragraph/math/bar/math-bar-pos.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// https://www.datypic.com/sc/ooxml/e-m_pos-1.html
|
||||||
|
import { Attributes, XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
|
export class MathBarPos extends XmlComponent {
|
||||||
|
// TODO: Use correct types rather than any
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
public constructor(attributes: any) {
|
||||||
|
super("m:pos");
|
||||||
|
this.root.push(new Attributes(attributes));
|
||||||
|
}
|
||||||
|
}
|
43
src/file/paragraph/math/bar/math-bar-properties.spec.ts
Normal file
43
src/file/paragraph/math/bar/math-bar-properties.spec.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
|
||||||
|
import { MathBarProperties } from "./math-bar-properties";
|
||||||
|
describe("MathBarProperties", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a MathBarProperties with top key", () => {
|
||||||
|
const mathBarProperties = new MathBarProperties("top");
|
||||||
|
|
||||||
|
const tree = new Formatter().format(mathBarProperties);
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"m:barPr": [
|
||||||
|
{
|
||||||
|
"m:pos": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": "top",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("should create a MathBarProperties with bottom key", () => {
|
||||||
|
const mathBarProperties = new MathBarProperties("bot");
|
||||||
|
|
||||||
|
const tree = new Formatter().format(mathBarProperties);
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"m:barPr": [
|
||||||
|
{
|
||||||
|
"m:pos": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": "bot",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
11
src/file/paragraph/math/bar/math-bar-properties.ts
Normal file
11
src/file/paragraph/math/bar/math-bar-properties.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// https://www.datypic.com/sc/ooxml/e-m_barPr-1.html
|
||||||
|
import { XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
|
import { MathBarPos } from "./math-bar-pos";
|
||||||
|
|
||||||
|
export class MathBarProperties extends XmlComponent {
|
||||||
|
public constructor(type: string) {
|
||||||
|
super("m:barPr");
|
||||||
|
this.root.push(new MathBarPos({ val: type }));
|
||||||
|
}
|
||||||
|
}
|
38
src/file/paragraph/math/bar/math-bar.spec.ts
Normal file
38
src/file/paragraph/math/bar/math-bar.spec.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
|
||||||
|
import { MathBar } from "./math-bar";
|
||||||
|
import { MathRun } from "../math-run";
|
||||||
|
|
||||||
|
describe("MathBar", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a MathBar with correct root key", () => {
|
||||||
|
const mathBar = new MathBar({ type: "top", children: [new MathRun("text")] });
|
||||||
|
const tree = new Formatter().format(mathBar);
|
||||||
|
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"m:bar": [
|
||||||
|
{
|
||||||
|
"m:barPr": [
|
||||||
|
{
|
||||||
|
"m:pos": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": "top",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"m:e": [
|
||||||
|
{
|
||||||
|
"m:r": [{ "m:t": ["text"] }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
18
src/file/paragraph/math/bar/math-bar.ts
Normal file
18
src/file/paragraph/math/bar/math-bar.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// https://www.datypic.com/sc/ooxml/e-m_bar-1.html
|
||||||
|
import { XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
|
import { MathBarProperties } from "./math-bar-properties";
|
||||||
|
import type { MathComponent } from "../math-component";
|
||||||
|
import { MathBase } from "../n-ary";
|
||||||
|
|
||||||
|
type MathBarOption = {
|
||||||
|
readonly type: "top" | "bot";
|
||||||
|
readonly children: readonly MathComponent[];
|
||||||
|
};
|
||||||
|
export class MathBar extends XmlComponent {
|
||||||
|
public constructor(options: MathBarOption) {
|
||||||
|
super("m:bar");
|
||||||
|
this.root.push(new MathBarProperties(options.type));
|
||||||
|
this.root.push(new MathBase(options.children));
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
import { XmlComponent } from "@file/xml-components";
|
import { XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
import { MathPreSubSuperScriptProperties } from "./math-pre-sub-super-script-function-properties";
|
import { MathPreSubSuperScriptProperties } from "./math-pre-sub-super-script-function-properties";
|
||||||
import { MathComponent } from "../../math-component";
|
import type { MathComponent } from "../../math-component";
|
||||||
import { MathBase, MathSubScriptElement, MathSuperScriptElement } from "../../n-ary";
|
import { MathBase, MathSubScriptElement, MathSuperScriptElement } from "../../n-ary";
|
||||||
|
|
||||||
export type IMathPreSubSuperScriptOptions = {
|
export type IMathPreSubSuperScriptOptions = {
|
||||||
|
@ -38,6 +38,8 @@ export type ILevelParagraphStylePropertiesOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type IParagraphStylePropertiesOptions = {
|
export type IParagraphStylePropertiesOptions = {
|
||||||
|
readonly border?: IBordersOptions;
|
||||||
|
readonly shading?: IShadingAttributesProperties;
|
||||||
readonly numbering?:
|
readonly numbering?:
|
||||||
| {
|
| {
|
||||||
readonly reference: string;
|
readonly reference: string;
|
||||||
@ -49,7 +51,6 @@ export type IParagraphStylePropertiesOptions = {
|
|||||||
} & ILevelParagraphStylePropertiesOptions;
|
} & ILevelParagraphStylePropertiesOptions;
|
||||||
|
|
||||||
export type IParagraphPropertiesOptions = {
|
export type IParagraphPropertiesOptions = {
|
||||||
readonly border?: IBordersOptions;
|
|
||||||
readonly heading?: (typeof HeadingLevel)[keyof typeof HeadingLevel];
|
readonly heading?: (typeof HeadingLevel)[keyof typeof HeadingLevel];
|
||||||
readonly bidirectional?: boolean;
|
readonly bidirectional?: boolean;
|
||||||
readonly pageBreakBefore?: boolean;
|
readonly pageBreakBefore?: boolean;
|
||||||
@ -58,7 +59,6 @@ export type IParagraphPropertiesOptions = {
|
|||||||
readonly bullet?: {
|
readonly bullet?: {
|
||||||
readonly level: number;
|
readonly level: number;
|
||||||
};
|
};
|
||||||
readonly shading?: IShadingAttributesProperties;
|
|
||||||
readonly widowControl?: boolean;
|
readonly widowControl?: boolean;
|
||||||
readonly frame?: IFrameOptions;
|
readonly frame?: IFrameOptions;
|
||||||
readonly suppressLineNumbers?: boolean;
|
readonly suppressLineNumbers?: boolean;
|
||||||
|
@ -11,6 +11,7 @@ import { ConcreteHyperlink, ExternalHyperlink, ParagraphChild } from "@file/para
|
|||||||
import { TargetModeType } from "@file/relationships/relationship/relationship";
|
import { TargetModeType } from "@file/relationships/relationship/relationship";
|
||||||
import { IContext } from "@file/xml-components";
|
import { IContext } from "@file/xml-components";
|
||||||
import { uniqueId } from "@util/convenience-functions";
|
import { uniqueId } from "@util/convenience-functions";
|
||||||
|
import { OutputByType, OutputType } from "@util/output-type";
|
||||||
|
|
||||||
import { appendContentType } from "./content-types-manager";
|
import { appendContentType } from "./content-types-manager";
|
||||||
import { appendRelationship, getNextRelationshipIndex } from "./relationship-manager";
|
import { appendRelationship, getNextRelationshipIndex } from "./relationship-manager";
|
||||||
@ -47,21 +48,7 @@ type IHyperlinkRelationshipAddition = {
|
|||||||
|
|
||||||
export type IPatch = ParagraphPatch | FilePatch;
|
export type IPatch = ParagraphPatch | FilePatch;
|
||||||
|
|
||||||
// From JSZip
|
export type PatchDocumentOutputType = OutputType;
|
||||||
type OutputByType = {
|
|
||||||
readonly base64: string;
|
|
||||||
// eslint-disable-next-line id-denylist
|
|
||||||
readonly string: string;
|
|
||||||
readonly text: string;
|
|
||||||
readonly binarystring: string;
|
|
||||||
readonly array: readonly number[];
|
|
||||||
readonly uint8array: Uint8Array;
|
|
||||||
readonly arraybuffer: ArrayBuffer;
|
|
||||||
readonly blob: Blob;
|
|
||||||
readonly nodebuffer: Buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PatchDocumentOutputType = keyof OutputByType;
|
|
||||||
|
|
||||||
export type PatchDocumentOptions<T extends PatchDocumentOutputType = PatchDocumentOutputType> = {
|
export type PatchDocumentOptions<T extends PatchDocumentOutputType = PatchDocumentOutputType> = {
|
||||||
readonly outputType: T;
|
readonly outputType: T;
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
export * from "./convenience-functions";
|
export * from "./convenience-functions";
|
||||||
export * from "./values";
|
export * from "./values";
|
||||||
|
export type * from "./output-type";
|
||||||
|
18
src/util/output-type.ts
Normal file
18
src/util/output-type.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/* v8 ignore start */
|
||||||
|
// Simply type definitions. Can ignore testing and coverage
|
||||||
|
// From JSZip
|
||||||
|
export type OutputByType = {
|
||||||
|
readonly base64: string;
|
||||||
|
// eslint-disable-next-line id-denylist
|
||||||
|
readonly string: string;
|
||||||
|
readonly text: string;
|
||||||
|
readonly binarystring: string;
|
||||||
|
readonly array: readonly number[];
|
||||||
|
readonly uint8array: Uint8Array;
|
||||||
|
readonly arraybuffer: ArrayBuffer;
|
||||||
|
readonly blob: Blob;
|
||||||
|
readonly nodebuffer: Buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type OutputType = keyof OutputByType;
|
||||||
|
/* v8 ignore stop */
|
Reference in New Issue
Block a user