Merge branch 'master' into feat/footnotes
# Conflicts: # demo/demo16.js # src/file/file.ts
This commit is contained in:
10
README.md
10
README.md
@ -88,18 +88,22 @@ exporter.packPdf("My First Document");
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
Check [the Wiki](https://github.com/dolanmiu/docx/wiki/Examples) for examples.
|
Check [the Wiki](https://github.com/dolanmiu/docx/wiki/Examples) and the [demo folder](https://github.com/dolanmiu/docx/tree/master/demo) for examples.
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Read the contribution guidelines [here](https://github.com/dolanmiu/docx/wiki/Contributing-Guidelines).
|
Read the contribution guidelines [here](https://github.com/dolanmiu/docx/wiki/Contributing-Guidelines).
|
||||||
|
|
||||||
|
# Honoured Mentions
|
||||||
|
|
||||||
|
[@felipeochoa](https://github.com/felipeochoa)
|
||||||
|
|
||||||
|
[@h4buli](https://github.com/h4buli)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Made with 💖
|
Made with 💖
|
||||||
|
|
||||||
Huge thanks to [@felipeochoa](https://github.com/felipeochoa) for awesome contributions to this project
|
|
||||||
|
|
||||||
[npm-image]: https://badge.fury.io/js/docx.svg
|
[npm-image]: https://badge.fury.io/js/docx.svg
|
||||||
[npm-url]: https://npmjs.org/package/docx
|
[npm-url]: https://npmjs.org/package/docx
|
||||||
[downloads-image]: https://img.shields.io/npm/dm/docx.svg
|
[downloads-image]: https://img.shields.io/npm/dm/docx.svg
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
const docx = require('../build');
|
const docx = require('../build');
|
||||||
|
|
||||||
var doc = new docx.Document(undefined,{differentFirstPageHeader:true});
|
var doc = new docx.Document();
|
||||||
|
|
||||||
doc.createParagraph("First Page").pageBreak()
|
doc.createParagraph("First Page").pageBreak()
|
||||||
doc.createParagraph("Second Page");
|
doc.createParagraph("Second Page");
|
||||||
@ -10,7 +10,8 @@ var pageNumber = new docx.TextRun().pageNumber()
|
|||||||
var pageoneheader = new docx.Paragraph("First Page Header ").right();
|
var pageoneheader = new docx.Paragraph("First Page Header ").right();
|
||||||
|
|
||||||
pageoneheader.addRun(pageNumber);
|
pageoneheader.addRun(pageNumber);
|
||||||
doc.firstPageHeader.addParagraph(pageoneheader);
|
var firstPageHeader = doc.createFirstPageHeader();
|
||||||
|
firstPageHeader.addParagraph(pageoneheader);
|
||||||
|
|
||||||
var pagetwoheader = new docx.Paragraph("My Title ").right();
|
var pagetwoheader = new docx.Paragraph("My Title ").right();
|
||||||
|
|
||||||
|
@ -1,14 +1,36 @@
|
|||||||
const docx = require('../build');
|
const docx = require("../build");
|
||||||
|
|
||||||
var doc = new docx.Document();
|
var doc = new docx.Document();
|
||||||
|
|
||||||
var paragraph = new docx.Paragraph("Hello World").referenceFootnote(1);
|
var paragraph = new docx.Paragraph("Hello World").pageBreak();
|
||||||
|
|
||||||
doc.addParagraph(paragraph);
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
doc.createFootnote(new docx.Paragraph("Test"));
|
var header = doc.createHeader();
|
||||||
|
header.createParagraph("Header on another page");
|
||||||
|
var footer = doc.createFooter();
|
||||||
|
footer.createParagraph("Footer on another page");
|
||||||
|
|
||||||
|
doc.addSection({
|
||||||
|
headerId: header.Header.referenceId,
|
||||||
|
footerId: footer.Footer.referenceId,
|
||||||
|
pageNumberStart: 1,
|
||||||
|
pageNumberFormatType: docx.PageNumberFormat.DECIMAL,
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.createParagraph("hello");
|
||||||
|
|
||||||
|
doc.addSection({
|
||||||
|
headerId: header.Header.referenceId,
|
||||||
|
footerId: footer.Footer.referenceId,
|
||||||
|
pageNumberStart: 1,
|
||||||
|
pageNumberFormatType: docx.PageNumberFormat.DECIMAL,
|
||||||
|
orientation: docx.PageOrientation.LANDSCAPE,
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.createParagraph("hello in landscape");
|
||||||
|
|
||||||
var exporter = new docx.LocalPacker(doc);
|
var exporter = new docx.LocalPacker(doc);
|
||||||
exporter.pack('My Document');
|
exporter.pack("My Document");
|
||||||
|
|
||||||
console.log('Document created successfully at project root!');
|
console.log("Document created successfully at project root!");
|
||||||
|
@ -65,6 +65,7 @@
|
|||||||
"awesome-typescript-loader": "^3.4.1",
|
"awesome-typescript-loader": "^3.4.1",
|
||||||
"chai": "^3.5.0",
|
"chai": "^3.5.0",
|
||||||
"glob": "^7.1.2",
|
"glob": "^7.1.2",
|
||||||
|
"jszip": "^3.1.5",
|
||||||
"mocha": "^3.2.0",
|
"mocha": "^3.2.0",
|
||||||
"mocha-webpack": "^1.0.1",
|
"mocha-webpack": "^1.0.1",
|
||||||
"prettier": "^1.12.1",
|
"prettier": "^1.12.1",
|
||||||
|
75
src/export/packer/compiler.spec.ts
Normal file
75
src/export/packer/compiler.spec.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/* tslint:disable:typedef space-before-function-paren */
|
||||||
|
import * as fs from "fs";
|
||||||
|
import * as JSZip from "jszip";
|
||||||
|
|
||||||
|
import { expect } from "chai";
|
||||||
|
import { File } from "../../file";
|
||||||
|
import { Compiler } from "./compiler";
|
||||||
|
|
||||||
|
describe("Compiler", () => {
|
||||||
|
let compiler: Compiler;
|
||||||
|
let file: File;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
file = new File();
|
||||||
|
compiler = new Compiler(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#compile()", () => {
|
||||||
|
it("should pack all the content", async function() {
|
||||||
|
this.timeout(99999999);
|
||||||
|
const fileName = "build/tests/test.docx";
|
||||||
|
await compiler.compile(fs.createWriteStream(fileName));
|
||||||
|
|
||||||
|
const docxFile = fs.readFileSync(fileName);
|
||||||
|
const zipFile: JSZip = await JSZip.loadAsync(docxFile);
|
||||||
|
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||||
|
|
||||||
|
expect(fileNames).is.an.instanceof(Array);
|
||||||
|
expect(fileNames).has.length(12);
|
||||||
|
expect(fileNames).to.include("word/document.xml");
|
||||||
|
expect(fileNames).to.include("word/styles.xml");
|
||||||
|
expect(fileNames).to.include("docProps/core.xml");
|
||||||
|
expect(fileNames).to.include("docProps/app.xml");
|
||||||
|
expect(fileNames).to.include("word/numbering.xml");
|
||||||
|
expect(fileNames).to.include("word/header1.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
||||||
|
expect(fileNames).to.include("word/footer1.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/footer1.xml.rels");
|
||||||
|
expect(fileNames).to.include("word/_rels/document.xml.rels");
|
||||||
|
expect(fileNames).to.include("[Content_Types].xml");
|
||||||
|
expect(fileNames).to.include("_rels/.rels");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should pack all additional headers and footers", async function() {
|
||||||
|
file.createFooter();
|
||||||
|
file.createFooter();
|
||||||
|
file.createHeader();
|
||||||
|
file.createHeader();
|
||||||
|
|
||||||
|
this.timeout(99999999);
|
||||||
|
const fileName = "build/tests/test2.docx";
|
||||||
|
await compiler.compile(fs.createWriteStream(fileName));
|
||||||
|
|
||||||
|
const docxFile = fs.readFileSync(fileName);
|
||||||
|
const zipFile: JSZip = await JSZip.loadAsync(docxFile);
|
||||||
|
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/header1.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
||||||
|
expect(fileNames).to.include("word/header2.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/header2.xml.rels");
|
||||||
|
expect(fileNames).to.include("word/header3.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/header3.xml.rels");
|
||||||
|
expect(fileNames).to.include("word/footer1.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/footer1.xml.rels");
|
||||||
|
expect(fileNames).to.include("word/footer2.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/footer2.xml.rels");
|
||||||
|
expect(fileNames).to.include("word/footer3.xml");
|
||||||
|
expect(fileNames).to.include("word/_rels/footer3.xml.rels");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -33,11 +33,6 @@ export class Compiler {
|
|||||||
const xmlNumbering = xml(this.formatter.format(this.file.Numbering));
|
const xmlNumbering = xml(this.formatter.format(this.file.Numbering));
|
||||||
const xmlRelationships = xml(this.formatter.format(this.file.DocumentRelationships));
|
const xmlRelationships = xml(this.formatter.format(this.file.DocumentRelationships));
|
||||||
const xmlFileRelationships = xml(this.formatter.format(this.file.FileRelationships));
|
const xmlFileRelationships = xml(this.formatter.format(this.file.FileRelationships));
|
||||||
const xmlHeader = xml(this.formatter.format(this.file.Header.Header));
|
|
||||||
const xmlHeader2 = xml(this.formatter.format(this.file.firstPageHeader.Header));
|
|
||||||
const xmlFooter = xml(this.formatter.format(this.file.Footer.Footer));
|
|
||||||
const xmlHeaderRelationships = xml(this.formatter.format(this.file.Header.Relationships));
|
|
||||||
const xmlFooterRelationships = xml(this.formatter.format(this.file.Footer.Relationships));
|
|
||||||
const xmlContentTypes = xml(this.formatter.format(this.file.ContentTypes));
|
const xmlContentTypes = xml(this.formatter.format(this.file.ContentTypes));
|
||||||
const xmlAppProperties = xml(this.formatter.format(this.file.AppProperties));
|
const xmlAppProperties = xml(this.formatter.format(this.file.AppProperties));
|
||||||
const xmlFootnotes = xml(this.formatter.format(this.file.FootNotes));
|
const xmlFootnotes = xml(this.formatter.format(this.file.FootNotes));
|
||||||
@ -62,17 +57,29 @@ export class Compiler {
|
|||||||
name: "word/numbering.xml",
|
name: "word/numbering.xml",
|
||||||
});
|
});
|
||||||
|
|
||||||
this.archive.append(xmlHeader, {
|
// headers
|
||||||
name: "word/header1.xml",
|
for (let i = 0; i < this.file.Headers.length; i++) {
|
||||||
|
const element = this.file.Headers[i];
|
||||||
|
this.archive.append(xml(this.formatter.format(element.Header)), {
|
||||||
|
name: `word/header${i + 1}.xml`,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.archive.append(xmlHeader2, {
|
this.archive.append(xml(this.formatter.format(element.Relationships)), {
|
||||||
name: "word/header2.xml",
|
name: `word/_rels/header${i + 1}.xml.rels`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// footers
|
||||||
|
for (let i = 0; i < this.file.Footers.length; i++) {
|
||||||
|
const element = this.file.Footers[i];
|
||||||
|
this.archive.append(xml(this.formatter.format(element.Footer)), {
|
||||||
|
name: `word/footer${i + 1}.xml`,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.archive.append(xmlFooter, {
|
this.archive.append(xml(this.formatter.format(element.Relationships)), {
|
||||||
name: "word/footer1.xml",
|
name: `word/_rels/footer${i + 1}.xml.rels`,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.archive.append(xmlFootnotes, {
|
this.archive.append(xmlFootnotes, {
|
||||||
name: "word/footnotes.xml",
|
name: "word/footnotes.xml",
|
||||||
@ -82,14 +89,6 @@ export class Compiler {
|
|||||||
name: "word/_rels/document.xml.rels",
|
name: "word/_rels/document.xml.rels",
|
||||||
});
|
});
|
||||||
|
|
||||||
this.archive.append(xmlHeaderRelationships, {
|
|
||||||
name: "word/_rels/header1.xml.rels",
|
|
||||||
});
|
|
||||||
|
|
||||||
this.archive.append(xmlFooterRelationships, {
|
|
||||||
name: "word/_rels/footer1.xml.rels",
|
|
||||||
});
|
|
||||||
|
|
||||||
this.archive.append(xmlContentTypes, {
|
this.archive.append(xmlContentTypes, {
|
||||||
name: "[Content_Types].xml",
|
name: "[Content_Types].xml",
|
||||||
});
|
});
|
||||||
|
139
src/file/content-types/content-types.spec.ts
Normal file
139
src/file/content-types/content-types.spec.ts
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
import { Formatter } from "../../export/formatter";
|
||||||
|
import { ContentTypes } from "./content-types";
|
||||||
|
describe("ContentTypes", () => {
|
||||||
|
let contentTypes: ContentTypes;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
contentTypes = new ContentTypes();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create default content types", () => {
|
||||||
|
const tree = new Formatter().format(contentTypes);
|
||||||
|
|
||||||
|
expect(tree["Types"]).to.be.an.instanceof(Array);
|
||||||
|
|
||||||
|
expect(tree["Types"][0]).to.deep.equal({ _attr: { xmlns: "http://schemas.openxmlformats.org/package/2006/content-types" } });
|
||||||
|
expect(tree["Types"][1]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/png", Extension: "png" } }] });
|
||||||
|
expect(tree["Types"][2]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/jpeg", Extension: "jpeg" } }] });
|
||||||
|
expect(tree["Types"][3]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/jpeg", Extension: "jpg" } }] });
|
||||||
|
expect(tree["Types"][4]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/bmp", Extension: "bmp" } }] });
|
||||||
|
expect(tree["Types"][5]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/gif", Extension: "gif" } }] });
|
||||||
|
expect(tree["Types"][6]).to.deep.equal({
|
||||||
|
Default: [{ _attr: { ContentType: "application/vnd.openxmlformats-package.relationships+xml", Extension: "rels" } }],
|
||||||
|
});
|
||||||
|
expect(tree["Types"][7]).to.deep.equal({ Default: [{ _attr: { ContentType: "application/xml", Extension: "xml" } }] });
|
||||||
|
expect(tree["Types"][8]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
|
||||||
|
PartName: "/word/document.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(tree["Types"][9]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
|
||||||
|
PartName: "/word/styles.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(tree["Types"][10]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-package.core-properties+xml",
|
||||||
|
PartName: "/docProps/core.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(tree["Types"][11]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||||
|
PartName: "/docProps/app.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(tree["Types"][12]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
|
||||||
|
PartName: "/word/numbering.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#addFooter()", () => {
|
||||||
|
it("should add footer", () => {
|
||||||
|
contentTypes.addFooter(101);
|
||||||
|
contentTypes.addFooter(102);
|
||||||
|
const tree = new Formatter().format(contentTypes);
|
||||||
|
|
||||||
|
expect(tree["Types"][13]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
||||||
|
PartName: "/word/footer101.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree["Types"][14]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
||||||
|
PartName: "/word/footer102.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#addHeader()", () => {
|
||||||
|
it("should add header", () => {
|
||||||
|
contentTypes.addHeader(201);
|
||||||
|
contentTypes.addHeader(202);
|
||||||
|
const tree = new Formatter().format(contentTypes);
|
||||||
|
|
||||||
|
expect(tree["Types"][13]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
||||||
|
PartName: "/word/header201.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree["Types"][14]).to.deep.equal({
|
||||||
|
Override: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
||||||
|
PartName: "/word/header202.xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -25,13 +25,22 @@ export class ContentTypes extends XmlComponent {
|
|||||||
new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", "/word/document.xml"),
|
new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", "/word/document.xml"),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml", "/word/header1.xml"));
|
|
||||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml", "/word/header2.xml"));
|
|
||||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml", "/word/footer1.xml"));
|
|
||||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", "/word/styles.xml"));
|
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", "/word/styles.xml"));
|
||||||
this.root.push(new Override("application/vnd.openxmlformats-package.core-properties+xml", "/docProps/core.xml"));
|
this.root.push(new Override("application/vnd.openxmlformats-package.core-properties+xml", "/docProps/core.xml"));
|
||||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.extended-properties+xml", "/docProps/app.xml"));
|
this.root.push(new Override("application/vnd.openxmlformats-officedocument.extended-properties+xml", "/docProps/app.xml"));
|
||||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", "/word/numbering.xml"));
|
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", "/word/numbering.xml"));
|
||||||
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", "/word/footnotes.xml"));
|
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", "/word/footnotes.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addFooter(index: number): void {
|
||||||
|
this.root.push(
|
||||||
|
new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml", `/word/footer${index}.xml`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addHeader(index: number): void {
|
||||||
|
this.root.push(
|
||||||
|
new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml", `/word/header${index}.xml`),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,42 @@
|
|||||||
// import { assert } from "chai";
|
import { expect } from "chai";
|
||||||
|
|
||||||
// import { Utility } from "../../../tests/utility";
|
import { Formatter } from "../../../export/formatter";
|
||||||
// import { Body } from "./";
|
import { Body } from "./body";
|
||||||
|
|
||||||
describe("Body", () => {
|
describe("Body", () => {
|
||||||
// let body: Body;
|
let body: Body;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// body = new Body();
|
body = new Body();
|
||||||
});
|
});
|
||||||
|
|
||||||
// describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
// it("should create the Section Properties", () => {
|
it("should create default section", () => {
|
||||||
// const newJson = Utility.jsonify(body);
|
const formatted = new Formatter().format(body)["w:body"][0];
|
||||||
// assert.equal(newJson.root[0].rootKey, "w:sectPr");
|
expect(formatted)
|
||||||
// });
|
.to.have.property("w:sectPr")
|
||||||
|
.and.to.be.an.instanceof(Array);
|
||||||
|
expect(formatted["w:sectPr"]).to.have.length(7);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// it("should create the Page Size", () => {
|
describe("addSection", () => {
|
||||||
// const newJson = Utility.jsonify(body);
|
it("should add section with options", () => {
|
||||||
// assert.equal(newJson.root[1].rootKey, "w:pgSz");
|
body.addSection({
|
||||||
// });
|
width: 10000,
|
||||||
|
height: 10000,
|
||||||
|
});
|
||||||
|
|
||||||
// it("should create the Page Margin", () => {
|
const formatted = new Formatter().format(body)["w:body"];
|
||||||
// const newJson = Utility.jsonify(body);
|
expect(formatted).to.be.an.instanceof(Array);
|
||||||
// assert.equal(newJson.root[2].rootKey, "w:pgMar");
|
const defaultSectionPr = formatted[0]["w:p"][1]["w:pPr"][0]["w:sectPr"];
|
||||||
// });
|
|
||||||
|
|
||||||
// it("should create the Columns", () => {
|
// check that this is the default section and added first in paragraph
|
||||||
// const newJson = Utility.jsonify(body);
|
expect(defaultSectionPr[0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
|
||||||
// assert.equal(newJson.root[3].rootKey, "w:cols");
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it("should create the Document Grid", () => {
|
// check for new section (since it's the last one, it's direct child of body)
|
||||||
// const newJson = Utility.jsonify(body);
|
const newSection = formatted[1]["w:sectPr"];
|
||||||
// assert.equal(newJson.root[4].rootKey, "w:docGrid");
|
expect(newSection[0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 10000, "w:w": 10000, "w:orient": "portrait" } }] });
|
||||||
// });
|
});
|
||||||
// });
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,14 +1,63 @@
|
|||||||
import { XmlComponent } from "file/xml-components";
|
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
||||||
import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties";
|
import { Paragraph, ParagraphProperties } from "../..";
|
||||||
|
import { SectionProperties, SectionPropertiesOptions } from "./section-properties";
|
||||||
|
|
||||||
export class Body extends XmlComponent {
|
export class Body extends XmlComponent {
|
||||||
|
private defaultSection: SectionProperties;
|
||||||
|
|
||||||
|
private sections: SectionProperties[] = [];
|
||||||
|
|
||||||
constructor(sectionPropertiesOptions?: SectionPropertiesOptions) {
|
constructor(sectionPropertiesOptions?: SectionPropertiesOptions) {
|
||||||
super("w:body");
|
super("w:body");
|
||||||
|
|
||||||
this.root.push(new SectionProperties(sectionPropertiesOptions));
|
this.defaultSection = new SectionProperties(sectionPropertiesOptions);
|
||||||
|
this.sections.push(this.defaultSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds new section properties.
|
||||||
|
* Note: Previous section is created in paragraph after the current element, and then new section will be added.
|
||||||
|
* The spec says:
|
||||||
|
* - section element should be in the last paragraph of the section
|
||||||
|
* - last section should be direct child of body
|
||||||
|
* @param section new section
|
||||||
|
*/
|
||||||
|
public addSection(section: SectionPropertiesOptions | SectionProperties): void {
|
||||||
|
const currentSection = this.sections.pop() as SectionProperties;
|
||||||
|
this.root.push(this.createSectionParagraph(currentSection));
|
||||||
|
if (section instanceof SectionProperties) {
|
||||||
|
this.sections.push(section);
|
||||||
|
} else {
|
||||||
|
const params = {
|
||||||
|
...this.defaultSection.Options,
|
||||||
|
...section,
|
||||||
|
};
|
||||||
|
this.sections.push(new SectionProperties(params));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public prepForXml(): IXmlableObject {
|
||||||
|
if (this.sections.length === 1) {
|
||||||
|
this.root.push(this.sections[0]);
|
||||||
|
} else if (this.sections.length > 1) {
|
||||||
|
throw new Error("Invalid usage of sections. At the end of the body element there must be ONE section.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.prepForXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
public push(component: XmlComponent): void {
|
public push(component: XmlComponent): void {
|
||||||
this.root.push(component);
|
this.root.push(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get DefaultSection(): SectionProperties {
|
||||||
|
return this.defaultSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createSectionParagraph(section: SectionProperties): Paragraph {
|
||||||
|
const paragraph = new Paragraph();
|
||||||
|
const properties = new ParagraphProperties();
|
||||||
|
properties.addChildElement(section);
|
||||||
|
paragraph.addChildElement(properties);
|
||||||
|
return paragraph;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from "./body";
|
export * from "./body";
|
||||||
|
export * from "./section-properties";
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { XmlAttributeComponent } from "file/xml-components";
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export enum FooterReferenceType {
|
||||||
|
DEFAULT = "default",
|
||||||
|
FIRST = "first",
|
||||||
|
EVEN = "even",
|
||||||
|
}
|
||||||
|
|
||||||
export interface IFooterReferenceAttributes {
|
export interface IFooterReferenceAttributes {
|
||||||
type: string;
|
type: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -1,13 +1,19 @@
|
|||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
import { FooterReferenceAttributes } from "./footer-reference-attributes";
|
import { FooterReferenceAttributes, FooterReferenceType } from "./footer-reference-attributes";
|
||||||
|
|
||||||
|
export interface IFooterOptions {
|
||||||
|
footerType?: FooterReferenceType;
|
||||||
|
footerId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class FooterReference extends XmlComponent {
|
export class FooterReference extends XmlComponent {
|
||||||
constructor() {
|
constructor(options: IFooterOptions) {
|
||||||
super("w:footerReference");
|
super("w:footerReference");
|
||||||
|
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new FooterReferenceAttributes({
|
new FooterReferenceAttributes({
|
||||||
type: "default",
|
type: options.footerType || FooterReferenceType.DEFAULT,
|
||||||
id: `rId${4}`,
|
id: `rId${options.footerId}`,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./footer-reference";
|
||||||
|
export * from "./footer-reference-attributes";
|
@ -1,5 +1,11 @@
|
|||||||
import { XmlAttributeComponent } from "file/xml-components";
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export enum HeaderReferenceType {
|
||||||
|
DEFAULT = "default",
|
||||||
|
FIRST = "first",
|
||||||
|
EVEN = "even",
|
||||||
|
}
|
||||||
|
|
||||||
export interface IHeaderReferenceAttributes {
|
export interface IHeaderReferenceAttributes {
|
||||||
type: string;
|
type: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
import { HeaderReferenceAttributes } from "./header-reference-attributes";
|
import { HeaderReferenceAttributes, HeaderReferenceType } from "./header-reference-attributes";
|
||||||
|
|
||||||
|
export interface IHeaderOptions {
|
||||||
|
headerType?: HeaderReferenceType;
|
||||||
|
headerId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class HeaderReference extends XmlComponent {
|
export class HeaderReference extends XmlComponent {
|
||||||
constructor(order: string, refID: number) {
|
constructor(options: IHeaderOptions) {
|
||||||
super("w:headerReference");
|
super("w:headerReference");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new HeaderReferenceAttributes({
|
new HeaderReferenceAttributes({
|
||||||
type: order,
|
type: options.headerType || HeaderReferenceType.DEFAULT,
|
||||||
id: `rId${refID}`,
|
id: `rId${options.headerId}`,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./header-reference";
|
||||||
|
export * from "./header-reference-attributes";
|
5
src/file/document/body/section-properties/index.ts
Normal file
5
src/file/document/body/section-properties/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export * from "./section-properties";
|
||||||
|
export * from "./footer-reference";
|
||||||
|
export * from "./header-reference";
|
||||||
|
export * from "./page-size";
|
||||||
|
export * from "./page-number";
|
@ -0,0 +1 @@
|
|||||||
|
export * from "./page-number";
|
@ -0,0 +1,41 @@
|
|||||||
|
// http://officeopenxml.com/WPSectionPgNumType.php
|
||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export enum PageNumberFormat {
|
||||||
|
CARDINAL_TEXT = "cardinalText",
|
||||||
|
DECIMAL = "decimal",
|
||||||
|
DECIMAL_ENCLOSED_CIRCLE = "decimalEnclosedCircle",
|
||||||
|
DECIMAL_ENCLOSED_FULL_STOP = "decimalEnclosedFullstop",
|
||||||
|
DECIMAL_ENCLOSED_PAREN = "decimalEnclosedParen",
|
||||||
|
DECIMAL_ZERO = "decimalZero",
|
||||||
|
LOWER_LETTER = "lowerLetter",
|
||||||
|
LOWER_ROMAN = "lowerRoman",
|
||||||
|
NONE = "none",
|
||||||
|
ORDINAL_TEXT = "ordinalText",
|
||||||
|
UPPER_LETTER = "upperLetter",
|
||||||
|
UPPER_ROMAN = "upperRoman",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPageNumberTypeAttributes {
|
||||||
|
pageNumberStart?: number;
|
||||||
|
pageNumberFormatType?: PageNumberFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PageNumberTypeAttributes extends XmlAttributeComponent<IPageNumberTypeAttributes> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
pageNumberStart: "w:start",
|
||||||
|
pageNumberFormatType: "w:fmt",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PageNumberType extends XmlComponent {
|
||||||
|
constructor(start?: number, numberFormat?: PageNumberFormat) {
|
||||||
|
super("w:pgNumType");
|
||||||
|
this.root.push(
|
||||||
|
new PageNumberTypeAttributes({
|
||||||
|
pageNumberStart: start,
|
||||||
|
pageNumberFormatType: numberFormat,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./page-size";
|
||||||
|
export * from "./page-size-attributes";
|
@ -1,9 +1,14 @@
|
|||||||
import { XmlAttributeComponent } from "file/xml-components";
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export enum PageOrientation {
|
||||||
|
PORTRAIT = "portrait",
|
||||||
|
LANDSCAPE = "landscape",
|
||||||
|
}
|
||||||
|
|
||||||
export interface IPageSizeAttributes {
|
export interface IPageSizeAttributes {
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
orientation?: string;
|
orientation?: PageOrientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PageSizeAttributes extends XmlAttributeComponent<IPageSizeAttributes> {
|
export class PageSizeAttributes extends XmlAttributeComponent<IPageSizeAttributes> {
|
||||||
|
@ -2,11 +2,12 @@ import { expect } from "chai";
|
|||||||
|
|
||||||
import { Formatter } from "../../../../../export/formatter";
|
import { Formatter } from "../../../../../export/formatter";
|
||||||
import { PageSize } from "./page-size";
|
import { PageSize } from "./page-size";
|
||||||
|
import { PageOrientation } from "./page-size-attributes";
|
||||||
|
|
||||||
describe("PageSize", () => {
|
describe("PageSize", () => {
|
||||||
describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
it("should create page size with portrait", () => {
|
it("should create page size with portrait", () => {
|
||||||
const properties = new PageSize(100, 200, "portrait");
|
const properties = new PageSize(100, 200, PageOrientation.PORTRAIT);
|
||||||
const tree = new Formatter().format(properties);
|
const tree = new Formatter().format(properties);
|
||||||
|
|
||||||
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
|
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
|
||||||
@ -15,7 +16,7 @@ describe("PageSize", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should create page size with horizontal and invert the lengths", () => {
|
it("should create page size with horizontal and invert the lengths", () => {
|
||||||
const properties = new PageSize(100, 200, "landscape");
|
const properties = new PageSize(100, 200, PageOrientation.LANDSCAPE);
|
||||||
const tree = new Formatter().format(properties);
|
const tree = new Formatter().format(properties);
|
||||||
|
|
||||||
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
|
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
import { PageSizeAttributes } from "./page-size-attributes";
|
import { PageOrientation, PageSizeAttributes } from "./page-size-attributes";
|
||||||
|
|
||||||
export class PageSize extends XmlComponent {
|
export class PageSize extends XmlComponent {
|
||||||
constructor(width: number, height: number, orientation: string) {
|
constructor(width: number, height: number, orientation: PageOrientation) {
|
||||||
super("w:pgSz");
|
super("w:pgSz");
|
||||||
|
|
||||||
const flip = orientation === "landscape";
|
const flip = orientation === PageOrientation.LANDSCAPE;
|
||||||
|
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new PageSizeAttributes({
|
new PageSizeAttributes({
|
||||||
|
@ -2,6 +2,7 @@ import { expect } from "chai";
|
|||||||
|
|
||||||
import { Formatter } from "../../../../export/formatter";
|
import { Formatter } from "../../../../export/formatter";
|
||||||
import { SectionProperties } from "./section-properties";
|
import { SectionProperties } from "./section-properties";
|
||||||
|
import { FooterReferenceType, PageNumberFormat } from ".";
|
||||||
|
|
||||||
describe("SectionProperties", () => {
|
describe("SectionProperties", () => {
|
||||||
describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
@ -18,6 +19,11 @@ describe("SectionProperties", () => {
|
|||||||
gutter: 0,
|
gutter: 0,
|
||||||
space: 708,
|
space: 708,
|
||||||
linePitch: 360,
|
linePitch: 360,
|
||||||
|
headerId: 100,
|
||||||
|
footerId: 200,
|
||||||
|
footerType: FooterReferenceType.EVEN,
|
||||||
|
pageNumberStart: 10,
|
||||||
|
pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT,
|
||||||
});
|
});
|
||||||
const tree = new Formatter().format(properties);
|
const tree = new Formatter().format(properties);
|
||||||
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
|
||||||
@ -38,6 +44,12 @@ describe("SectionProperties", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": [{ _attr: { "w:space": 708 } }] });
|
||||||
|
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] });
|
||||||
|
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:headerReference": [{ _attr: { "r:id": "rId100", "w:type": "default" } }] });
|
||||||
|
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": [{ _attr: { "r:id": "rId200", "w:type": "even" } }] });
|
||||||
|
expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "cardinalText", "w:start": 10 } }] });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create section properties with no options", () => {
|
it("should create section properties with no options", () => {
|
||||||
@ -61,6 +73,11 @@ describe("SectionProperties", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": [{ _attr: { "w:space": 708 } }] });
|
||||||
|
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] });
|
||||||
|
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:headerReference": [{ _attr: { "r:id": "rId0", "w:type": "default" } }] });
|
||||||
|
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": [{ _attr: { "r:id": "rId0", "w:type": "default" } }] });
|
||||||
|
expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create section properties with changed options", () => {
|
it("should create section properties with changed options", () => {
|
||||||
|
@ -1,20 +1,29 @@
|
|||||||
// http://officeopenxml.com/WPsection.php
|
// http://officeopenxml.com/WPsection.php
|
||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { FooterReferenceType, IPageNumberTypeAttributes, PageNumberFormat, PageNumberType } from "./";
|
||||||
import { Columns } from "./columns/columns";
|
import { Columns } from "./columns/columns";
|
||||||
import { IColumnsAttributes } from "./columns/columns-attributes";
|
import { IColumnsAttributes } from "./columns/columns-attributes";
|
||||||
import { DocumentGrid } from "./doc-grid/doc-grid";
|
import { DocumentGrid } from "./doc-grid/doc-grid";
|
||||||
import { IDocGridAttributesProperties } from "./doc-grid/doc-grid-attributes";
|
import { IDocGridAttributesProperties } from "./doc-grid/doc-grid-attributes";
|
||||||
import { FooterReference } from "./footer-reference/footer-reference";
|
import { FooterReference, IFooterOptions } from "./footer-reference/footer-reference";
|
||||||
import { HeaderReference } from "./header-reference/header-reference";
|
import { HeaderReference, IHeaderOptions } from "./header-reference/header-reference";
|
||||||
|
import { HeaderReferenceType } from "./header-reference/header-reference-attributes";
|
||||||
import { PageMargin } from "./page-margin/page-margin";
|
import { PageMargin } from "./page-margin/page-margin";
|
||||||
import { IPageMarginAttributes } from "./page-margin/page-margin-attributes";
|
import { IPageMarginAttributes } from "./page-margin/page-margin-attributes";
|
||||||
import { PageSize } from "./page-size/page-size";
|
import { PageSize } from "./page-size/page-size";
|
||||||
import { IPageSizeAttributes } from "./page-size/page-size-attributes";
|
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
|
||||||
import { TitlePage } from "./title-page/title-page";
|
// import { TitlePage } from "./title-page/title-page";
|
||||||
|
|
||||||
export type SectionPropertiesOptions = IPageSizeAttributes & IPageMarginAttributes & IColumnsAttributes & IDocGridAttributesProperties;
|
export type SectionPropertiesOptions = IPageSizeAttributes &
|
||||||
|
IPageMarginAttributes &
|
||||||
|
IColumnsAttributes &
|
||||||
|
IDocGridAttributesProperties &
|
||||||
|
IHeaderOptions &
|
||||||
|
IFooterOptions &
|
||||||
|
IPageNumberTypeAttributes;
|
||||||
|
|
||||||
export class SectionProperties extends XmlComponent {
|
export class SectionProperties extends XmlComponent {
|
||||||
|
private options: SectionPropertiesOptions;
|
||||||
constructor(options?: SectionPropertiesOptions) {
|
constructor(options?: SectionPropertiesOptions) {
|
||||||
super("w:sectPr");
|
super("w:sectPr");
|
||||||
|
|
||||||
@ -30,8 +39,13 @@ export class SectionProperties extends XmlComponent {
|
|||||||
gutter: 0,
|
gutter: 0,
|
||||||
space: 708,
|
space: 708,
|
||||||
linePitch: 360,
|
linePitch: 360,
|
||||||
orientation: "portrait",
|
orientation: PageOrientation.PORTRAIT,
|
||||||
differentFirstPageHeader: false,
|
headerType: HeaderReferenceType.DEFAULT,
|
||||||
|
headerId: 0,
|
||||||
|
footerType: FooterReferenceType.DEFAULT,
|
||||||
|
footerId: 0,
|
||||||
|
pageNumberStart: undefined,
|
||||||
|
pageNumberFormatType: PageNumberFormat.DECIMAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mergedOptions = {
|
const mergedOptions = {
|
||||||
@ -53,13 +67,26 @@ export class SectionProperties extends XmlComponent {
|
|||||||
);
|
);
|
||||||
this.root.push(new Columns(mergedOptions.space));
|
this.root.push(new Columns(mergedOptions.space));
|
||||||
this.root.push(new DocumentGrid(mergedOptions.linePitch));
|
this.root.push(new DocumentGrid(mergedOptions.linePitch));
|
||||||
this.root.push(new HeaderReference("default", 3));
|
|
||||||
|
|
||||||
if (mergedOptions.differentFirstPageHeader) {
|
this.root.push(
|
||||||
this.root.push(new HeaderReference("first", 5));
|
new HeaderReference({
|
||||||
this.root.push(new TitlePage());
|
headerType: mergedOptions.headerType,
|
||||||
|
headerId: mergedOptions.headerId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
this.root.push(
|
||||||
|
new FooterReference({
|
||||||
|
footerType: mergedOptions.footerType,
|
||||||
|
footerId: mergedOptions.footerId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.root.push(new PageNumberType(mergedOptions.pageNumberStart, mergedOptions.pageNumberFormatType));
|
||||||
|
|
||||||
|
this.options = mergedOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.root.push(new FooterReference());
|
get Options(): SectionPropertiesOptions {
|
||||||
|
return this.options;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,11 @@ describe("Document", () => {
|
|||||||
}
|
}
|
||||||
assert.isTrue(true);
|
assert.isTrue(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should create default section", () => {
|
||||||
|
const body = new Formatter().format(document)["w:document"][1]["w:body"];
|
||||||
|
expect(body[0]).to.have.property("w:sectPr");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#createParagraph", () => {
|
describe("#createParagraph", () => {
|
||||||
@ -33,7 +38,7 @@ describe("Document", () => {
|
|||||||
expect(body)
|
expect(body)
|
||||||
.to.be.an("array")
|
.to.be.an("array")
|
||||||
.which.has.length.at.least(1);
|
.which.has.length.at.least(1);
|
||||||
expect(body[1]).to.have.property("w:p");
|
expect(body[0]).to.have.property("w:p");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should use the text given to create a run in the paragraph", () => {
|
it("should use the text given to create a run in the paragraph", () => {
|
||||||
@ -43,7 +48,7 @@ describe("Document", () => {
|
|||||||
expect(body)
|
expect(body)
|
||||||
.to.be.an("array")
|
.to.be.an("array")
|
||||||
.which.has.length.at.least(1);
|
.which.has.length.at.least(1);
|
||||||
expect(body[1])
|
expect(body[0])
|
||||||
.to.have.property("w:p")
|
.to.have.property("w:p")
|
||||||
.which.includes({
|
.which.includes({
|
||||||
"w:r": [{ "w:rPr": [] }, { "w:t": [{ _attr: { "xml:space": "preserve" } }, "sample paragraph text"] }],
|
"w:r": [{ "w:rPr": [] }, { "w:t": [{ _attr: { "xml:space": "preserve" } }, "sample paragraph text"] }],
|
||||||
@ -59,7 +64,7 @@ describe("Document", () => {
|
|||||||
expect(body)
|
expect(body)
|
||||||
.to.be.an("array")
|
.to.be.an("array")
|
||||||
.which.has.length.at.least(1);
|
.which.has.length.at.least(1);
|
||||||
expect(body[1]).to.have.property("w:tbl");
|
expect(body[0]).to.have.property("w:tbl");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should create a table with the correct dimensions", () => {
|
it("should create a table with the correct dimensions", () => {
|
||||||
@ -68,7 +73,7 @@ describe("Document", () => {
|
|||||||
expect(body)
|
expect(body)
|
||||||
.to.be.an("array")
|
.to.be.an("array")
|
||||||
.which.has.length.at.least(1);
|
.which.has.length.at.least(1);
|
||||||
expect(body[1])
|
expect(body[0])
|
||||||
.to.have.property("w:tbl")
|
.to.have.property("w:tbl")
|
||||||
.which.includes({
|
.which.includes({
|
||||||
"w:tblGrid": [
|
"w:tblGrid": [
|
||||||
@ -77,7 +82,7 @@ describe("Document", () => {
|
|||||||
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
|
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
expect(body[1]["w:tbl"].filter((x) => x["w:tr"])).to.have.length(2);
|
expect(body[0]["w:tbl"].filter((x) => x["w:tr"])).to.have.length(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -70,4 +70,8 @@ export class Document extends XmlComponent {
|
|||||||
|
|
||||||
return run;
|
return run;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get Body(): Body {
|
||||||
|
return this.body;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from "./document";
|
export * from "./document";
|
||||||
|
export * from "./body";
|
||||||
|
26
src/file/drawing/anchor/anchor-attributes.ts
Normal file
26
src/file/drawing/anchor/anchor-attributes.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
import { IDistance } from "../drawing";
|
||||||
|
|
||||||
|
export interface IAnchorAttributes extends IDistance {
|
||||||
|
allowOverlap?: "0" | "1";
|
||||||
|
behindDoc?: "0" | "1";
|
||||||
|
layoutInCell?: "0" | "1";
|
||||||
|
locked?: "0" | "1";
|
||||||
|
relativeHeight?: number;
|
||||||
|
simplePos?: "0" | "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AnchorAttributes extends XmlAttributeComponent<IAnchorAttributes> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
distT: "distT",
|
||||||
|
distB: "distB",
|
||||||
|
distL: "distL",
|
||||||
|
distR: "distR",
|
||||||
|
allowOverlap: "allowOverlap",
|
||||||
|
behindDoc: "behindDoc",
|
||||||
|
layoutInCell: "layoutInCell",
|
||||||
|
locked: "locked",
|
||||||
|
relativeHeight: "relativeHeight",
|
||||||
|
simplePos: "simplePos",
|
||||||
|
};
|
||||||
|
}
|
118
src/file/drawing/anchor/anchor.spec.ts
Normal file
118
src/file/drawing/anchor/anchor.spec.ts
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import { assert } from "chai";
|
||||||
|
|
||||||
|
import { Utility } from "../../../tests/utility";
|
||||||
|
import { IDrawingOptions, TextWrapStyle } from ".././";
|
||||||
|
import { Anchor } from "./";
|
||||||
|
|
||||||
|
function createDrawing(drawingOptions: IDrawingOptions): Anchor {
|
||||||
|
return new Anchor(
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
pixels: {
|
||||||
|
x: 100,
|
||||||
|
y: 100,
|
||||||
|
},
|
||||||
|
emus: {
|
||||||
|
x: 100 * 9525,
|
||||||
|
y: 100 * 9525,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
drawingOptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Anchor", () => {
|
||||||
|
let anchor: Anchor;
|
||||||
|
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a Drawing with correct root key", () => {
|
||||||
|
anchor = createDrawing({});
|
||||||
|
const newJson = Utility.jsonify(anchor);
|
||||||
|
assert.equal(newJson.rootKey, "wp:anchor");
|
||||||
|
assert.equal(newJson.root.length, 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a Drawing with all default options", () => {
|
||||||
|
anchor = createDrawing({});
|
||||||
|
const newJson = Utility.jsonify(anchor);
|
||||||
|
assert.equal(newJson.root.length, 10);
|
||||||
|
|
||||||
|
const anchorAttributes = newJson.root[0].root;
|
||||||
|
assert.include(anchorAttributes, {
|
||||||
|
distT: 0,
|
||||||
|
distB: 0,
|
||||||
|
distL: 0,
|
||||||
|
distR: 0,
|
||||||
|
simplePos: "0",
|
||||||
|
allowOverlap: "1",
|
||||||
|
behindDoc: "0",
|
||||||
|
locked: "0",
|
||||||
|
layoutInCell: "1",
|
||||||
|
relativeHeight: 952500,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 1: simple pos
|
||||||
|
assert.equal(newJson.root[1].rootKey, "wp:simplePos");
|
||||||
|
|
||||||
|
// 2: horizontal position
|
||||||
|
const horizontalPosition = newJson.root[2];
|
||||||
|
assert.equal(horizontalPosition.rootKey, "wp:positionH");
|
||||||
|
assert.include(horizontalPosition.root[0].root, {
|
||||||
|
relativeFrom: "column",
|
||||||
|
});
|
||||||
|
assert.equal(horizontalPosition.root[1].rootKey, "wp:posOffset");
|
||||||
|
assert.include(horizontalPosition.root[1].root[0], 0);
|
||||||
|
|
||||||
|
// 3: vertical position
|
||||||
|
const verticalPosition = newJson.root[3];
|
||||||
|
assert.equal(verticalPosition.rootKey, "wp:positionV");
|
||||||
|
assert.include(verticalPosition.root[0].root, {
|
||||||
|
relativeFrom: "paragraph",
|
||||||
|
});
|
||||||
|
assert.equal(verticalPosition.root[1].rootKey, "wp:posOffset");
|
||||||
|
assert.include(verticalPosition.root[1].root[0], 0);
|
||||||
|
|
||||||
|
// 4: extent
|
||||||
|
const extent = newJson.root[4];
|
||||||
|
assert.equal(extent.rootKey, "wp:extent");
|
||||||
|
assert.include(extent.root[0].root, {
|
||||||
|
cx: 952500,
|
||||||
|
cy: 952500,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5: effect extent
|
||||||
|
const effectExtent = newJson.root[5];
|
||||||
|
assert.equal(effectExtent.rootKey, "wp:effectExtent");
|
||||||
|
|
||||||
|
// 6 text wrap: none
|
||||||
|
const textWrap = newJson.root[6];
|
||||||
|
assert.equal(textWrap.rootKey, "wp:wrapNone");
|
||||||
|
|
||||||
|
// 7: doc properties
|
||||||
|
const docProperties = newJson.root[7];
|
||||||
|
assert.equal(docProperties.rootKey, "wp:docPr");
|
||||||
|
|
||||||
|
// 8: graphic frame properties
|
||||||
|
const graphicFrame = newJson.root[8];
|
||||||
|
assert.equal(graphicFrame.rootKey, "wp:cNvGraphicFramePr");
|
||||||
|
|
||||||
|
// 9: graphic
|
||||||
|
const graphic = newJson.root[9];
|
||||||
|
assert.equal(graphic.rootKey, "a:graphic");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a Drawing with text wrapping", () => {
|
||||||
|
anchor = createDrawing({
|
||||||
|
textWrapping: {
|
||||||
|
textWrapStyle: TextWrapStyle.SQUARE,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const newJson = Utility.jsonify(anchor);
|
||||||
|
assert.equal(newJson.root.length, 10);
|
||||||
|
|
||||||
|
// 6 text wrap: square
|
||||||
|
const textWrap = newJson.root[6];
|
||||||
|
assert.equal(textWrap.rootKey, "wp:wrapSquare");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
88
src/file/drawing/anchor/anchor.ts
Normal file
88
src/file/drawing/anchor/anchor.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating.php
|
||||||
|
import { IMediaDataDimensions } from "file/media";
|
||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { IDrawingOptions } from "../drawing";
|
||||||
|
import {
|
||||||
|
HorizontalPosition,
|
||||||
|
HorizontalPositionRelativeFrom,
|
||||||
|
IFloating,
|
||||||
|
SimplePos,
|
||||||
|
VerticalPosition,
|
||||||
|
VerticalPositionRelativeFrom,
|
||||||
|
} from "../floating";
|
||||||
|
import { Graphic } from "../inline/graphic";
|
||||||
|
import { TextWrapStyle, WrapNone, WrapSquare, WrapTight, WrapTopAndBottom } from "../text-wrap";
|
||||||
|
import { DocProperties } from "./../doc-properties/doc-properties";
|
||||||
|
import { EffectExtent } from "./../effect-extent/effect-extent";
|
||||||
|
import { Extent } from "./../extent/extent";
|
||||||
|
import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties";
|
||||||
|
import { AnchorAttributes } from "./anchor-attributes";
|
||||||
|
|
||||||
|
const defaultOptions: IFloating = {
|
||||||
|
allowOverlap: true,
|
||||||
|
behindDocument: false,
|
||||||
|
lockAnchor: false,
|
||||||
|
layoutInCell: true,
|
||||||
|
verticalPosition: {
|
||||||
|
relative: VerticalPositionRelativeFrom.PARAGRAPH,
|
||||||
|
offset: 0,
|
||||||
|
},
|
||||||
|
horizontalPosition: {
|
||||||
|
relative: HorizontalPositionRelativeFrom.COLUMN,
|
||||||
|
offset: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Anchor extends XmlComponent {
|
||||||
|
constructor(referenceId: number, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) {
|
||||||
|
super("wp:anchor");
|
||||||
|
|
||||||
|
const floating = {
|
||||||
|
...defaultOptions,
|
||||||
|
...drawingOptions.floating,
|
||||||
|
};
|
||||||
|
this.root.push(
|
||||||
|
new AnchorAttributes({
|
||||||
|
distT: 0,
|
||||||
|
distB: 0,
|
||||||
|
distL: 0,
|
||||||
|
distR: 0,
|
||||||
|
simplePos: "0", // note: word doesn't fully support - so we use 0
|
||||||
|
allowOverlap: floating.allowOverlap === true ? "1" : "0",
|
||||||
|
behindDoc: floating.behindDocument === true ? "1" : "0",
|
||||||
|
locked: floating.lockAnchor === true ? "1" : "0",
|
||||||
|
layoutInCell: floating.layoutInCell === true ? "1" : "0",
|
||||||
|
relativeHeight: dimensions.emus.y,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.root.push(new SimplePos());
|
||||||
|
this.root.push(new HorizontalPosition(floating.horizontalPosition));
|
||||||
|
this.root.push(new VerticalPosition(floating.verticalPosition));
|
||||||
|
this.root.push(new Extent(dimensions.emus.x, dimensions.emus.y));
|
||||||
|
this.root.push(new EffectExtent());
|
||||||
|
|
||||||
|
if (drawingOptions.textWrapping != null) {
|
||||||
|
switch (drawingOptions.textWrapping.textWrapStyle) {
|
||||||
|
case TextWrapStyle.SQUARE:
|
||||||
|
this.root.push(new WrapSquare(drawingOptions.textWrapping));
|
||||||
|
break;
|
||||||
|
case TextWrapStyle.TIGHT:
|
||||||
|
this.root.push(new WrapTight(drawingOptions.textWrapping.distanceFromText));
|
||||||
|
break;
|
||||||
|
case TextWrapStyle.TOP_AND_BOTTOM:
|
||||||
|
this.root.push(new WrapTopAndBottom(drawingOptions.textWrapping.distanceFromText));
|
||||||
|
break;
|
||||||
|
case TextWrapStyle.NONE:
|
||||||
|
default:
|
||||||
|
this.root.push(new WrapNone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.root.push(new WrapNone());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.root.push(new DocProperties());
|
||||||
|
this.root.push(new GraphicFrameProperties());
|
||||||
|
this.root.push(new Graphic(referenceId, dimensions.emus.x, dimensions.emus.y));
|
||||||
|
}
|
||||||
|
}
|
2
src/file/drawing/anchor/index.ts
Normal file
2
src/file/drawing/anchor/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./anchor";
|
||||||
|
export * from "./anchor-attributes";
|
@ -2,14 +2,12 @@ import { assert } from "chai";
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
|
|
||||||
import { Utility } from "../../tests/utility";
|
import { Utility } from "../../tests/utility";
|
||||||
import { Drawing } from "./";
|
import { Drawing, IDrawingOptions, PlacementPosition } from "./";
|
||||||
|
|
||||||
describe("Drawing", () => {
|
function createDrawing(drawingOptions?: IDrawingOptions): Drawing {
|
||||||
let currentBreak: Drawing;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
const path = "./demo/images/image1.jpeg";
|
const path = "./demo/images/image1.jpeg";
|
||||||
currentBreak = new Drawing({
|
return new Drawing(
|
||||||
|
{
|
||||||
fileName: "test.jpg",
|
fileName: "test.jpg",
|
||||||
referenceId: 1,
|
referenceId: 1,
|
||||||
stream: fs.createReadStream(path),
|
stream: fs.createReadStream(path),
|
||||||
@ -24,14 +22,33 @@ describe("Drawing", () => {
|
|||||||
y: 100 * 9525,
|
y: 100 * 9525,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
});
|
drawingOptions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Drawing", () => {
|
||||||
|
let currentBreak: Drawing;
|
||||||
|
|
||||||
describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
it("should create a Drawing with correct root key", () => {
|
it("should create a Drawing with correct root key", () => {
|
||||||
|
currentBreak = createDrawing();
|
||||||
const newJson = Utility.jsonify(currentBreak);
|
const newJson = Utility.jsonify(currentBreak);
|
||||||
assert.equal(newJson.rootKey, "w:drawing");
|
assert.equal(newJson.rootKey, "w:drawing");
|
||||||
// console.log(JSON.stringify(newJson, null, 2));
|
});
|
||||||
|
|
||||||
|
it("should create a drawing with inline element when there are no options passed", () => {
|
||||||
|
currentBreak = createDrawing();
|
||||||
|
const newJson = Utility.jsonify(currentBreak);
|
||||||
|
assert.equal(newJson.root[0].rootKey, "wp:inline");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a drawing with anchor element when there options are passed", () => {
|
||||||
|
currentBreak = createDrawing({
|
||||||
|
position: PlacementPosition.FLOATING,
|
||||||
|
});
|
||||||
|
const newJson = Utility.jsonify(currentBreak);
|
||||||
|
assert.equal(newJson.root[0].rootKey, "wp:anchor");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,20 +1,53 @@
|
|||||||
import { IMediaData } from "file/media";
|
import { IMediaData } from "file/media";
|
||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { Anchor } from "./anchor";
|
||||||
|
import { IFloating } from "./floating";
|
||||||
import { Inline } from "./inline";
|
import { Inline } from "./inline";
|
||||||
|
import { ITextWrapping } from "./text-wrap";
|
||||||
|
|
||||||
|
export enum PlacementPosition {
|
||||||
|
INLINE,
|
||||||
|
FLOATING,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDistance {
|
||||||
|
distT?: number;
|
||||||
|
distB?: number;
|
||||||
|
distL?: number;
|
||||||
|
distR?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDrawingOptions {
|
||||||
|
position?: PlacementPosition;
|
||||||
|
textWrapping?: ITextWrapping;
|
||||||
|
floating?: IFloating;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultDrawingOptions: IDrawingOptions = {
|
||||||
|
position: PlacementPosition.INLINE,
|
||||||
|
};
|
||||||
|
|
||||||
export class Drawing extends XmlComponent {
|
export class Drawing extends XmlComponent {
|
||||||
private inline: Inline;
|
private inline: Inline;
|
||||||
|
|
||||||
constructor(imageData: IMediaData) {
|
constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) {
|
||||||
super("w:drawing");
|
super("w:drawing");
|
||||||
|
|
||||||
if (imageData === undefined) {
|
if (imageData === undefined) {
|
||||||
throw new Error("imageData cannot be undefined");
|
throw new Error("imageData cannot be undefined");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inline = new Inline(imageData.referenceId, imageData.dimensions);
|
const mergedOptions = {
|
||||||
|
...defaultDrawingOptions,
|
||||||
|
...drawingOptions,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (mergedOptions.position === PlacementPosition.INLINE) {
|
||||||
|
this.inline = new Inline(imageData.referenceId, imageData.dimensions);
|
||||||
this.root.push(this.inline);
|
this.root.push(this.inline);
|
||||||
|
} else if (mergedOptions.position === PlacementPosition.FLOATING) {
|
||||||
|
this.root.push(new Anchor(imageData.referenceId, imageData.dimensions, mergedOptions));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public scale(factorX: number, factorY: number): void {
|
public scale(factorX: number, factorY: number): void {
|
||||||
|
15
src/file/drawing/floating/align.spec.ts
Normal file
15
src/file/drawing/floating/align.spec.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { assert } from "chai";
|
||||||
|
|
||||||
|
import { VerticalPositionAlign } from ".";
|
||||||
|
import { Utility } from "../../../tests/utility";
|
||||||
|
import { Align } from "./align";
|
||||||
|
|
||||||
|
describe("Align", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a element with correct root key", () => {
|
||||||
|
const newJson = Utility.jsonify(new Align(VerticalPositionAlign.CENTER));
|
||||||
|
assert.equal(newJson.rootKey, "wp:align");
|
||||||
|
assert.include(newJson.root[0], VerticalPositionAlign.CENTER);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
10
src/file/drawing/floating/align.ts
Normal file
10
src/file/drawing/floating/align.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-position.php
|
||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { HorizontalPositionAlign, VerticalPositionAlign } from "./floating-position";
|
||||||
|
|
||||||
|
export class Align extends XmlComponent {
|
||||||
|
constructor(value: HorizontalPositionAlign | VerticalPositionAlign) {
|
||||||
|
super("wp:align");
|
||||||
|
this.root.push(value);
|
||||||
|
}
|
||||||
|
}
|
60
src/file/drawing/floating/floating-position.ts
Normal file
60
src/file/drawing/floating/floating-position.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-position.php
|
||||||
|
|
||||||
|
export enum HorizontalPositionRelativeFrom {
|
||||||
|
CHARACTER = "character",
|
||||||
|
COLUMN = "column",
|
||||||
|
INSIDE_MARGIN = "insideMargin",
|
||||||
|
LEFT_MARGIN = "leftMargin",
|
||||||
|
MARGIN = "margin",
|
||||||
|
OUTSIDE_MARGIN = "outsideMargin",
|
||||||
|
PAGE = "page",
|
||||||
|
RIGHT_MARGIN = "rightMargin",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum VerticalPositionRelativeFrom {
|
||||||
|
BOTTOM_MARGIN = "bottomMargin",
|
||||||
|
INSIDE_MARGIN = "insideMargin",
|
||||||
|
LINE = "line",
|
||||||
|
MARGIN = "margin",
|
||||||
|
OUTSIDE_MARGIN = "outsideMargin",
|
||||||
|
PAGE = "page",
|
||||||
|
PARAGRAPH = "paragraph",
|
||||||
|
TOP_MARGIN = "topMargin",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum HorizontalPositionAlign {
|
||||||
|
CENTER = "center",
|
||||||
|
INSIDE = "inside",
|
||||||
|
LEFT = "left",
|
||||||
|
OUTSIDE = "outside",
|
||||||
|
RIGHT = "right",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum VerticalPositionAlign {
|
||||||
|
BOTTOM = "bottom",
|
||||||
|
CENTER = "center",
|
||||||
|
INSIDE = "inside",
|
||||||
|
OUTSIDE = "outside",
|
||||||
|
TOP = "top",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IHorizontalPositionOptions {
|
||||||
|
relative: HorizontalPositionRelativeFrom;
|
||||||
|
align?: HorizontalPositionAlign;
|
||||||
|
offset?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVerticalPositionOptions {
|
||||||
|
relative: VerticalPositionRelativeFrom;
|
||||||
|
align?: VerticalPositionAlign;
|
||||||
|
offset?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IFloating {
|
||||||
|
horizontalPosition: IHorizontalPositionOptions;
|
||||||
|
verticalPosition: IVerticalPositionOptions;
|
||||||
|
allowOverlap?: boolean;
|
||||||
|
lockAnchor?: boolean;
|
||||||
|
behindDocument?: boolean;
|
||||||
|
layoutInCell?: boolean;
|
||||||
|
}
|
41
src/file/drawing/floating/horizontal-position.spec.ts
Normal file
41
src/file/drawing/floating/horizontal-position.spec.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { assert } from "chai";
|
||||||
|
|
||||||
|
import { HorizontalPositionAlign, HorizontalPositionRelativeFrom } from ".";
|
||||||
|
import { Utility } from "../../../tests/utility";
|
||||||
|
import { HorizontalPosition } from "./horizontal-position";
|
||||||
|
|
||||||
|
describe("HorizontalPosition", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a element with position align", () => {
|
||||||
|
const newJson = Utility.jsonify(
|
||||||
|
new HorizontalPosition({
|
||||||
|
relative: HorizontalPositionRelativeFrom.MARGIN,
|
||||||
|
align: HorizontalPositionAlign.CENTER,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
assert.equal(newJson.rootKey, "wp:positionH");
|
||||||
|
assert.include(newJson.root[0].root, {
|
||||||
|
relativeFrom: "margin",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(newJson.root[1].rootKey, "wp:align");
|
||||||
|
assert.include(newJson.root[1].root, "center");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a element with offset", () => {
|
||||||
|
const newJson = Utility.jsonify(
|
||||||
|
new HorizontalPosition({
|
||||||
|
relative: HorizontalPositionRelativeFrom.MARGIN,
|
||||||
|
offset: 40,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
assert.equal(newJson.rootKey, "wp:positionH");
|
||||||
|
assert.include(newJson.root[0].root, {
|
||||||
|
relativeFrom: "margin",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(newJson.root[1].rootKey, "wp:posOffset");
|
||||||
|
assert.include(newJson.root[1].root[0], 40);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
35
src/file/drawing/floating/horizontal-position.ts
Normal file
35
src/file/drawing/floating/horizontal-position.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-position.php
|
||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
import { Align } from "./align";
|
||||||
|
import { HorizontalPositionRelativeFrom, IHorizontalPositionOptions } from "./floating-position";
|
||||||
|
import { PositionOffset } from "./position-offset";
|
||||||
|
|
||||||
|
interface IHorizontalPositionAttributes {
|
||||||
|
relativeFrom: HorizontalPositionRelativeFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
class HorizontalPositionAttributes extends XmlAttributeComponent<IHorizontalPositionAttributes> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
relativeFrom: "relativeFrom",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HorizontalPosition extends XmlComponent {
|
||||||
|
constructor(horizontalPosition: IHorizontalPositionOptions) {
|
||||||
|
super("wp:positionH");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new HorizontalPositionAttributes({
|
||||||
|
relativeFrom: horizontalPosition.relative,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (horizontalPosition.align) {
|
||||||
|
this.root.push(new Align(horizontalPosition.align));
|
||||||
|
} else if (horizontalPosition.offset !== undefined) {
|
||||||
|
this.root.push(new PositionOffset(horizontalPosition.offset));
|
||||||
|
} else {
|
||||||
|
throw new Error("There is no configuration provided for floating position (Align or offset)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
src/file/drawing/floating/index.ts
Normal file
4
src/file/drawing/floating/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from "./floating-position";
|
||||||
|
export * from "./simple-pos";
|
||||||
|
export * from "./horizontal-position";
|
||||||
|
export * from "./vertical-position";
|
14
src/file/drawing/floating/position-offset.spec.ts
Normal file
14
src/file/drawing/floating/position-offset.spec.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { assert } from "chai";
|
||||||
|
|
||||||
|
import { Utility } from "../../../tests/utility";
|
||||||
|
import { PositionOffset } from "./position-offset";
|
||||||
|
|
||||||
|
describe("PositionOffset", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a element with correct root key", () => {
|
||||||
|
const newJson = Utility.jsonify(new PositionOffset(50));
|
||||||
|
assert.equal(newJson.rootKey, "wp:posOffset");
|
||||||
|
assert.equal(newJson.root[0], 50);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
9
src/file/drawing/floating/position-offset.ts
Normal file
9
src/file/drawing/floating/position-offset.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-position.php
|
||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export class PositionOffset extends XmlComponent {
|
||||||
|
constructor(offsetValue: number) {
|
||||||
|
super("wp:posOffset");
|
||||||
|
this.root.push(offsetValue.toString());
|
||||||
|
}
|
||||||
|
}
|
17
src/file/drawing/floating/simple-pos.spec.ts
Normal file
17
src/file/drawing/floating/simple-pos.spec.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { assert } from "chai";
|
||||||
|
|
||||||
|
import { SimplePos } from "./simple-pos";
|
||||||
|
import { Utility } from "../../../tests/utility";
|
||||||
|
|
||||||
|
describe("SimplePos", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a element with correct root key", () => {
|
||||||
|
const newJson = Utility.jsonify(new SimplePos());
|
||||||
|
assert.equal(newJson.rootKey, "wp:simplePos");
|
||||||
|
assert.include(newJson.root[0].root, {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
28
src/file/drawing/floating/simple-pos.ts
Normal file
28
src/file/drawing/floating/simple-pos.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-position.php
|
||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
interface ISimplePosAttributes {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimplePosAttributes extends XmlAttributeComponent<ISimplePosAttributes> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
x: "x",
|
||||||
|
y: "y",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SimplePos extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("wp:simplePos");
|
||||||
|
|
||||||
|
// NOTE: It's not fully supported in Microsoft Word, but this element is needed anyway
|
||||||
|
this.root.push(
|
||||||
|
new SimplePosAttributes({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
41
src/file/drawing/floating/vertical-position.spec.ts
Normal file
41
src/file/drawing/floating/vertical-position.spec.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { assert } from "chai";
|
||||||
|
|
||||||
|
import { VerticalPositionAlign, VerticalPositionRelativeFrom } from ".";
|
||||||
|
import { Utility } from "../../../tests/utility";
|
||||||
|
import { VerticalPosition } from "./vertical-position";
|
||||||
|
|
||||||
|
describe("VerticalPosition", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a element with position align", () => {
|
||||||
|
const newJson = Utility.jsonify(
|
||||||
|
new VerticalPosition({
|
||||||
|
relative: VerticalPositionRelativeFrom.MARGIN,
|
||||||
|
align: VerticalPositionAlign.INSIDE,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
assert.equal(newJson.rootKey, "wp:positionV");
|
||||||
|
assert.include(newJson.root[0].root, {
|
||||||
|
relativeFrom: "margin",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(newJson.root[1].rootKey, "wp:align");
|
||||||
|
assert.include(newJson.root[1].root, "inside");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a element with offset", () => {
|
||||||
|
const newJson = Utility.jsonify(
|
||||||
|
new VerticalPosition({
|
||||||
|
relative: VerticalPositionRelativeFrom.MARGIN,
|
||||||
|
offset: 40,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
assert.equal(newJson.rootKey, "wp:positionV");
|
||||||
|
assert.include(newJson.root[0].root, {
|
||||||
|
relativeFrom: "margin",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(newJson.root[1].rootKey, "wp:posOffset");
|
||||||
|
assert.include(newJson.root[1].root[0], 40);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
35
src/file/drawing/floating/vertical-position.ts
Normal file
35
src/file/drawing/floating/vertical-position.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-position.php
|
||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
import { Align } from "./align";
|
||||||
|
import { IVerticalPositionOptions, VerticalPositionRelativeFrom } from "./floating-position";
|
||||||
|
import { PositionOffset } from "./position-offset";
|
||||||
|
|
||||||
|
interface IVerticalPositionAttributes {
|
||||||
|
relativeFrom: VerticalPositionRelativeFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
class VerticalPositionAttributes extends XmlAttributeComponent<IVerticalPositionAttributes> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
relativeFrom: "relativeFrom",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VerticalPosition extends XmlComponent {
|
||||||
|
constructor(verticalPosition: IVerticalPositionOptions) {
|
||||||
|
super("wp:positionV");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new VerticalPositionAttributes({
|
||||||
|
relativeFrom: verticalPosition.relative,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (verticalPosition.align) {
|
||||||
|
this.root.push(new Align(verticalPosition.align));
|
||||||
|
} else if (verticalPosition.offset !== undefined) {
|
||||||
|
this.root.push(new PositionOffset(verticalPosition.offset));
|
||||||
|
} else {
|
||||||
|
throw new Error("There is no configuration provided for floating position (Align or offset)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,3 @@
|
|||||||
export { Drawing } from "./drawing";
|
export * from "./drawing";
|
||||||
|
export * from "./text-wrap";
|
||||||
|
export * from "./floating";
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import { XmlAttributeComponent } from "file/xml-components";
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
import { IDistance } from "../drawing";
|
||||||
|
|
||||||
export interface IInlineAttributes {
|
// tslint:disable-next-line:no-empty-interface
|
||||||
distT?: number;
|
export interface IInlineAttributes extends IDistance {}
|
||||||
distB?: number;
|
|
||||||
distL?: number;
|
|
||||||
distR?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class InlineAttributes extends XmlAttributeComponent<IInlineAttributes> {
|
export class InlineAttributes extends XmlAttributeComponent<IInlineAttributes> {
|
||||||
protected xmlKeys = {
|
protected xmlKeys = {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// http://officeopenxml.com/drwPicInline.php
|
// http://officeopenxml.com/drwPicInline.php
|
||||||
import { IMediaDataDimensions } from "file/media";
|
import { IMediaDataDimensions } from "file/media";
|
||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
import { DocProperties } from "./doc-properties/doc-properties";
|
import { DocProperties } from "./../doc-properties/doc-properties";
|
||||||
import { EffectExtent } from "./effect-extent/effect-extent";
|
import { EffectExtent } from "./../effect-extent/effect-extent";
|
||||||
import { Extent } from "./extent/extent";
|
import { Extent } from "./../extent/extent";
|
||||||
import { Graphic } from "./graphic";
|
import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties";
|
||||||
import { GraphicFrameProperties } from "./graphic-frame/graphic-frame-properties";
|
import { Graphic } from "./../inline/graphic";
|
||||||
import { InlineAttributes } from "./inline-attributes";
|
import { InlineAttributes } from "./inline-attributes";
|
||||||
|
|
||||||
export class Inline extends XmlComponent {
|
export class Inline extends XmlComponent {
|
||||||
|
5
src/file/drawing/text-wrap/index.ts
Normal file
5
src/file/drawing/text-wrap/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export * from "./text-wrapping";
|
||||||
|
export * from "./wrap-none";
|
||||||
|
export * from "./wrap-square";
|
||||||
|
export * from "./wrap-tight";
|
||||||
|
export * from "./wrap-top-and-bottom";
|
22
src/file/drawing/text-wrap/text-wrapping.ts
Normal file
22
src/file/drawing/text-wrap/text-wrapping.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-textWrap.php
|
||||||
|
import { IDistance } from "../drawing";
|
||||||
|
|
||||||
|
export enum TextWrapStyle {
|
||||||
|
NONE,
|
||||||
|
SQUARE,
|
||||||
|
TIGHT,
|
||||||
|
TOP_AND_BOTTOM,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum WrapTextOption {
|
||||||
|
BOTH_SIDES = "bothSides",
|
||||||
|
LEFT = "left",
|
||||||
|
RIGHT = "right",
|
||||||
|
LARGEST = "largest",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ITextWrapping {
|
||||||
|
textWrapStyle: TextWrapStyle;
|
||||||
|
wrapTextOption?: WrapTextOption;
|
||||||
|
distanceFromText?: IDistance;
|
||||||
|
}
|
8
src/file/drawing/text-wrap/wrap-none.ts
Normal file
8
src/file/drawing/text-wrap/wrap-none.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-textWrap.php
|
||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export class WrapNone extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("wp:wrapNone");
|
||||||
|
}
|
||||||
|
}
|
31
src/file/drawing/text-wrap/wrap-square.ts
Normal file
31
src/file/drawing/text-wrap/wrap-square.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-textWrap.php
|
||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
import { ITextWrapping, WrapTextOption } from ".";
|
||||||
|
import { IDistance } from "../drawing";
|
||||||
|
|
||||||
|
interface IWrapSquareAttributes extends IDistance {
|
||||||
|
wrapText?: WrapTextOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WrapSquareAttributes extends XmlAttributeComponent<IWrapSquareAttributes> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
distT: "distT",
|
||||||
|
distB: "distB",
|
||||||
|
distL: "distL",
|
||||||
|
distR: "distR",
|
||||||
|
wrapText: "wrapText",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WrapSquare extends XmlComponent {
|
||||||
|
constructor(textWrapping: ITextWrapping) {
|
||||||
|
super("wp:wrapSquare");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new WrapSquareAttributes({
|
||||||
|
wrapText: textWrapping.wrapTextOption || WrapTextOption.BOTH_SIDES,
|
||||||
|
...textWrapping.distanceFromText,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
33
src/file/drawing/text-wrap/wrap-tight.ts
Normal file
33
src/file/drawing/text-wrap/wrap-tight.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-textWrap.php
|
||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
import { IDistance } from "../drawing";
|
||||||
|
|
||||||
|
interface IWrapTightAttributes {
|
||||||
|
distT?: number;
|
||||||
|
distB?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WrapTightAttributes extends XmlAttributeComponent<IWrapTightAttributes> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
distT: "distT",
|
||||||
|
distB: "distB",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WrapTight extends XmlComponent {
|
||||||
|
constructor(distanceFromText?: IDistance) {
|
||||||
|
super("wp:wrapTight");
|
||||||
|
|
||||||
|
distanceFromText = distanceFromText || {
|
||||||
|
distT: 0,
|
||||||
|
distB: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new WrapTightAttributes({
|
||||||
|
distT: distanceFromText.distT,
|
||||||
|
distB: distanceFromText.distB,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
33
src/file/drawing/text-wrap/wrap-top-and-bottom.ts
Normal file
33
src/file/drawing/text-wrap/wrap-top-and-bottom.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-textWrap.php
|
||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
import { IDistance } from "../drawing";
|
||||||
|
|
||||||
|
interface IWrapTopAndBottomAttributes {
|
||||||
|
distT?: number;
|
||||||
|
distB?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WrapTopAndBottomAttributes extends XmlAttributeComponent<IWrapTopAndBottomAttributes> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
distT: "distT",
|
||||||
|
distB: "distB",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WrapTopAndBottom extends XmlComponent {
|
||||||
|
constructor(distanceFromText?: IDistance) {
|
||||||
|
super("wp:wrapTopAndBottom");
|
||||||
|
|
||||||
|
distanceFromText = distanceFromText || {
|
||||||
|
distT: 0,
|
||||||
|
distB: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new WrapTopAndBottomAttributes({
|
||||||
|
distT: distanceFromText.distT,
|
||||||
|
distB: distanceFromText.distB,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
131
src/file/file.ts
131
src/file/file.ts
@ -3,10 +3,11 @@ import { AppProperties } from "./app-properties/app-properties";
|
|||||||
import { ContentTypes } from "./content-types/content-types";
|
import { ContentTypes } from "./content-types/content-types";
|
||||||
import { CoreProperties, IPropertiesOptions } from "./core-properties";
|
import { CoreProperties, IPropertiesOptions } from "./core-properties";
|
||||||
import { Document } from "./document";
|
import { Document } from "./document";
|
||||||
|
import { FooterReferenceType, HeaderReference, HeaderReferenceType } from "./document/body/section-properties";
|
||||||
import { SectionPropertiesOptions } from "./document/body/section-properties/section-properties";
|
import { SectionPropertiesOptions } from "./document/body/section-properties/section-properties";
|
||||||
import { FooterWrapper } from "./footer-wrapper";
|
import { FooterWrapper } from "./footer-wrapper";
|
||||||
import { FootNotes } from "./footnotes";
|
import { FootNotes } from "./footnotes";
|
||||||
import { FirstPageHeaderWrapper, HeaderWrapper } from "./header-wrapper";
|
import { HeaderWrapper } from "./header-wrapper";
|
||||||
import { Media } from "./media";
|
import { Media } from "./media";
|
||||||
import { Numbering } from "./numbering";
|
import { Numbering } from "./numbering";
|
||||||
import { Hyperlink, Paragraph, PictureRun } from "./paragraph";
|
import { Hyperlink, Paragraph, PictureRun } from "./paragraph";
|
||||||
@ -24,18 +25,16 @@ export class File {
|
|||||||
private readonly media: Media;
|
private readonly media: Media;
|
||||||
private readonly docRelationships: Relationships;
|
private readonly docRelationships: Relationships;
|
||||||
private readonly fileRelationships: Relationships;
|
private readonly fileRelationships: Relationships;
|
||||||
private readonly headerWrapper: HeaderWrapper;
|
private readonly headerWrapper: HeaderWrapper[] = [];
|
||||||
|
private readonly footerWrapper: FooterWrapper[] = [];
|
||||||
private readonly footNotes: FootNotes;
|
private readonly footNotes: FootNotes;
|
||||||
|
|
||||||
private readonly firstPageHeaderWrapper: FirstPageHeaderWrapper;
|
|
||||||
|
|
||||||
private readonly footerWrapper: FooterWrapper;
|
|
||||||
private readonly contentTypes: ContentTypes;
|
private readonly contentTypes: ContentTypes;
|
||||||
private readonly appProperties: AppProperties;
|
private readonly appProperties: AppProperties;
|
||||||
|
|
||||||
constructor(options?: IPropertiesOptions, sectionPropertiesOptions?: SectionPropertiesOptions) {
|
private nextId: number = 1;
|
||||||
this.document = new Document(sectionPropertiesOptions);
|
|
||||||
|
|
||||||
|
constructor(options?: IPropertiesOptions, sectionPropertiesOptions?: SectionPropertiesOptions) {
|
||||||
if (!options) {
|
if (!options) {
|
||||||
options = {
|
options = {
|
||||||
creator: "Un-named",
|
creator: "Un-named",
|
||||||
@ -56,45 +55,27 @@ export class File {
|
|||||||
this.numbering = new Numbering();
|
this.numbering = new Numbering();
|
||||||
this.docRelationships = new Relationships();
|
this.docRelationships = new Relationships();
|
||||||
this.docRelationships.createRelationship(
|
this.docRelationships.createRelationship(
|
||||||
1,
|
this.nextId++,
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
|
||||||
"styles.xml",
|
"styles.xml",
|
||||||
);
|
);
|
||||||
this.docRelationships.createRelationship(
|
this.docRelationships.createRelationship(
|
||||||
2,
|
this.nextId++,
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering",
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering",
|
||||||
"numbering.xml",
|
"numbering.xml",
|
||||||
);
|
);
|
||||||
this.docRelationships.createRelationship(
|
this.contentTypes = new ContentTypes();
|
||||||
3,
|
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
|
|
||||||
"header1.xml",
|
|
||||||
);
|
|
||||||
|
|
||||||
this.docRelationships.createRelationship(
|
this.docRelationships.createRelationship(
|
||||||
5,
|
this.nextId++,
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
|
|
||||||
"header2.xml",
|
|
||||||
);
|
|
||||||
|
|
||||||
this.docRelationships.createRelationship(
|
|
||||||
4,
|
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer",
|
|
||||||
"footer1.xml",
|
|
||||||
);
|
|
||||||
|
|
||||||
this.docRelationships.createRelationship(
|
|
||||||
6,
|
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
|
||||||
"footnotes.xml",
|
"footnotes.xml",
|
||||||
);
|
);
|
||||||
this.media = new Media();
|
this.media = new Media();
|
||||||
|
|
||||||
this.headerWrapper = new HeaderWrapper(this.media);
|
const header = this.createHeader();
|
||||||
this.firstPageHeaderWrapper = new FirstPageHeaderWrapper(this.media);
|
const footer = this.createFooter();
|
||||||
|
|
||||||
this.footerWrapper = new FooterWrapper(this.media);
|
|
||||||
this.contentTypes = new ContentTypes();
|
|
||||||
this.fileRelationships = new Relationships();
|
this.fileRelationships = new Relationships();
|
||||||
this.fileRelationships.createRelationship(
|
this.fileRelationships.createRelationship(
|
||||||
1,
|
1,
|
||||||
@ -112,7 +93,20 @@ export class File {
|
|||||||
"docProps/app.xml",
|
"docProps/app.xml",
|
||||||
);
|
);
|
||||||
this.appProperties = new AppProperties();
|
this.appProperties = new AppProperties();
|
||||||
|
|
||||||
this.footNotes = new FootNotes();
|
this.footNotes = new FootNotes();
|
||||||
|
if (!sectionPropertiesOptions) {
|
||||||
|
sectionPropertiesOptions = {
|
||||||
|
footerType: FooterReferenceType.DEFAULT,
|
||||||
|
headerType: HeaderReferenceType.DEFAULT,
|
||||||
|
headerId: header.Header.referenceId,
|
||||||
|
footerId: footer.Footer.referenceId,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
sectionPropertiesOptions.headerId = header.Header.referenceId;
|
||||||
|
sectionPropertiesOptions.footerId = footer.Footer.referenceId;
|
||||||
|
}
|
||||||
|
this.document = new Document(sectionPropertiesOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addParagraph(paragraph: Paragraph): void {
|
public addParagraph(paragraph: Paragraph): void {
|
||||||
@ -132,7 +126,7 @@ export class File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public createImage(image: string): PictureRun {
|
public createImage(image: string): PictureRun {
|
||||||
const mediaData = this.media.addMedia(image, this.docRelationships.RelationshipCount);
|
const mediaData = this.media.addMedia(image, this.nextId++);
|
||||||
this.docRelationships.createRelationship(
|
this.docRelationships.createRelationship(
|
||||||
mediaData.referenceId,
|
mediaData.referenceId,
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||||
@ -142,7 +136,7 @@ export class File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public createImageData(imageName: string, data: Buffer, width?: number, height?: number): IMediaData {
|
public createImageData(imageName: string, data: Buffer, width?: number, height?: number): IMediaData {
|
||||||
const mediaData = this.media.addMediaWithData(imageName, data, this.docRelationships.RelationshipCount, width, height);
|
const mediaData = this.media.addMediaWithData(imageName, data, this.nextId++, width, height);
|
||||||
this.docRelationships.createRelationship(
|
this.docRelationships.createRelationship(
|
||||||
mediaData.referenceId,
|
mediaData.referenceId,
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||||
@ -163,10 +157,57 @@ export class File {
|
|||||||
return hyperlink;
|
return hyperlink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addSection(sectionPropertiesOptions: SectionPropertiesOptions): void {
|
||||||
|
this.document.Body.addSection(sectionPropertiesOptions);
|
||||||
|
}
|
||||||
|
|
||||||
public createFootnote(paragraph: Paragraph): void {
|
public createFootnote(paragraph: Paragraph): void {
|
||||||
this.footNotes.createFootNote(paragraph);
|
this.footNotes.createFootNote(paragraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new header.
|
||||||
|
*/
|
||||||
|
public createHeader(): HeaderWrapper {
|
||||||
|
const header = new HeaderWrapper(this.media, this.nextId++);
|
||||||
|
this.headerWrapper.push(header);
|
||||||
|
this.docRelationships.createRelationship(
|
||||||
|
header.Header.referenceId,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
|
||||||
|
`header${this.headerWrapper.length}.xml`,
|
||||||
|
);
|
||||||
|
this.contentTypes.addHeader(this.headerWrapper.length);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new footer.
|
||||||
|
*/
|
||||||
|
public createFooter(): FooterWrapper {
|
||||||
|
const footer = new FooterWrapper(this.media, this.nextId++);
|
||||||
|
this.footerWrapper.push(footer);
|
||||||
|
this.docRelationships.createRelationship(
|
||||||
|
footer.Footer.referenceId,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer",
|
||||||
|
`footer${this.footerWrapper.length}.xml`,
|
||||||
|
);
|
||||||
|
this.contentTypes.addFooter(this.footerWrapper.length);
|
||||||
|
return footer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createFirstPageHeader(): HeaderWrapper {
|
||||||
|
const headerWrapper = this.createHeader();
|
||||||
|
|
||||||
|
this.document.Body.DefaultSection.addChildElement(
|
||||||
|
new HeaderReference({
|
||||||
|
headerType: HeaderReferenceType.FIRST,
|
||||||
|
headerId: headerWrapper.Header.referenceId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return headerWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
public get Document(): Document {
|
public get Document(): Document {
|
||||||
return this.document;
|
return this.document;
|
||||||
}
|
}
|
||||||
@ -196,17 +237,37 @@ export class File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get Header(): HeaderWrapper {
|
public get Header(): HeaderWrapper {
|
||||||
|
return this.headerWrapper[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public get Headers(): HeaderWrapper[] {
|
||||||
return this.headerWrapper;
|
return this.headerWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get firstPageHeader(): FirstPageHeaderWrapper {
|
public HeaderByRefNumber(refId: number): HeaderWrapper {
|
||||||
return this.firstPageHeaderWrapper;
|
const entry = this.headerWrapper.find((h) => h.Header.referenceId === refId);
|
||||||
|
if (entry) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
throw new Error(`There is no header with given reference id ${refId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get Footer(): FooterWrapper {
|
public get Footer(): FooterWrapper {
|
||||||
|
return this.footerWrapper[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public get Footers(): FooterWrapper[] {
|
||||||
return this.footerWrapper;
|
return this.footerWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FooterByRefNumber(refId: number): FooterWrapper {
|
||||||
|
const entry = this.footerWrapper.find((h) => h.Footer.referenceId === refId);
|
||||||
|
if (entry) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
throw new Error(`There is no footer with given reference id ${refId}`);
|
||||||
|
}
|
||||||
|
|
||||||
public get ContentTypes(): ContentTypes {
|
public get ContentTypes(): ContentTypes {
|
||||||
return this.contentTypes;
|
return this.contentTypes;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
import { Footer } from "./footer/footer";
|
import { Footer } from "./footer/footer";
|
||||||
import { IMediaData, Media } from "./media";
|
import { IMediaData, Media } from "./media";
|
||||||
import { Paragraph } from "./paragraph";
|
import { Paragraph } from "./paragraph";
|
||||||
@ -8,8 +9,8 @@ export class FooterWrapper {
|
|||||||
private readonly footer: Footer;
|
private readonly footer: Footer;
|
||||||
private readonly relationships: Relationships;
|
private readonly relationships: Relationships;
|
||||||
|
|
||||||
constructor(private readonly media: Media) {
|
constructor(private readonly media: Media, referenceId: number) {
|
||||||
this.footer = new Footer();
|
this.footer = new Footer(referenceId);
|
||||||
this.relationships = new Relationships();
|
this.relationships = new Relationships();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,6 +36,10 @@ export class FooterWrapper {
|
|||||||
this.footer.addDrawing(imageData);
|
this.footer.addDrawing(imageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addChildElement(childElement: XmlComponent | string): void {
|
||||||
|
this.footer.addChildElement(childElement);
|
||||||
|
}
|
||||||
|
|
||||||
public createImage(image: string): void {
|
public createImage(image: string): void {
|
||||||
const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount);
|
const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount);
|
||||||
this.relationships.createRelationship(
|
this.relationships.createRelationship(
|
||||||
|
@ -6,8 +6,10 @@ import { Table } from "../table";
|
|||||||
import { FooterAttributes } from "./footer-attributes";
|
import { FooterAttributes } from "./footer-attributes";
|
||||||
|
|
||||||
export class Footer extends XmlComponent {
|
export class Footer extends XmlComponent {
|
||||||
constructor() {
|
private refId: number;
|
||||||
|
constructor(referenceNumber: number) {
|
||||||
super("w:ftr");
|
super("w:ftr");
|
||||||
|
this.refId = referenceNumber;
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new FooterAttributes({
|
new FooterAttributes({
|
||||||
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
|
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
|
||||||
@ -30,6 +32,10 @@ export class Footer extends XmlComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get referenceId(): number {
|
||||||
|
return this.refId;
|
||||||
|
}
|
||||||
|
|
||||||
public addParagraph(paragraph: Paragraph): void {
|
public addParagraph(paragraph: Paragraph): void {
|
||||||
this.root.push(paragraph);
|
this.root.push(paragraph);
|
||||||
}
|
}
|
||||||
|
@ -1,65 +1,16 @@
|
|||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
import { Header } from "./header/header";
|
import { Header } from "./header/header";
|
||||||
import { IMediaData, Media } from "./media";
|
import { IMediaData, Media } from "./media";
|
||||||
import { Paragraph } from "./paragraph";
|
import { Paragraph } from "./paragraph";
|
||||||
import { Relationships } from "./relationships";
|
import { Relationships } from "./relationships";
|
||||||
import { Table } from "./table";
|
import { Table } from "./table";
|
||||||
|
|
||||||
export class FirstPageHeaderWrapper {
|
|
||||||
private readonly header: Header;
|
|
||||||
private readonly relationships: Relationships;
|
|
||||||
|
|
||||||
constructor(private readonly media: Media) {
|
|
||||||
this.header = new Header();
|
|
||||||
this.relationships = new Relationships();
|
|
||||||
}
|
|
||||||
|
|
||||||
public addParagraph(paragraph: Paragraph): void {
|
|
||||||
this.header.addParagraph(paragraph);
|
|
||||||
}
|
|
||||||
|
|
||||||
public createParagraph(text?: string): Paragraph {
|
|
||||||
const para = new Paragraph(text);
|
|
||||||
this.addParagraph(para);
|
|
||||||
return para;
|
|
||||||
}
|
|
||||||
|
|
||||||
public addTable(table: Table): void {
|
|
||||||
this.header.addTable(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
public createTable(rows: number, cols: number): Table {
|
|
||||||
return this.header.createTable(rows, cols);
|
|
||||||
}
|
|
||||||
|
|
||||||
public addDrawing(imageData: IMediaData): void {
|
|
||||||
this.header.addDrawing(imageData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public createImage(image: string): void {
|
|
||||||
const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount);
|
|
||||||
this.relationships.createRelationship(
|
|
||||||
mediaData.referenceId,
|
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
|
||||||
`media/${mediaData.fileName}`,
|
|
||||||
);
|
|
||||||
this.addDrawing(mediaData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get Header(): Header {
|
|
||||||
return this.header;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get Relationships(): Relationships {
|
|
||||||
return this.relationships;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HeaderWrapper {
|
export class HeaderWrapper {
|
||||||
private readonly header: Header;
|
private readonly header: Header;
|
||||||
private readonly relationships: Relationships;
|
private readonly relationships: Relationships;
|
||||||
|
|
||||||
constructor(private readonly media: Media) {
|
constructor(private readonly media: Media, referenceId: number) {
|
||||||
this.header = new Header();
|
this.header = new Header(referenceId);
|
||||||
this.relationships = new Relationships();
|
this.relationships = new Relationships();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,6 +36,10 @@ export class HeaderWrapper {
|
|||||||
this.header.addDrawing(imageData);
|
this.header.addDrawing(imageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addChildElement(childElement: XmlComponent | string): void {
|
||||||
|
this.header.addChildElement(childElement);
|
||||||
|
}
|
||||||
|
|
||||||
public createImage(image: string): void {
|
public createImage(image: string): void {
|
||||||
const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount);
|
const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount);
|
||||||
this.relationships.createRelationship(
|
this.relationships.createRelationship(
|
||||||
|
@ -6,8 +6,10 @@ import { Table } from "../table";
|
|||||||
import { HeaderAttributes } from "./header-attributes";
|
import { HeaderAttributes } from "./header-attributes";
|
||||||
|
|
||||||
export class Header extends XmlComponent {
|
export class Header extends XmlComponent {
|
||||||
constructor() {
|
private refId: number;
|
||||||
|
constructor(referenceNumber: number) {
|
||||||
super("w:hdr");
|
super("w:hdr");
|
||||||
|
this.refId = referenceNumber;
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new HeaderAttributes({
|
new HeaderAttributes({
|
||||||
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
|
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
|
||||||
@ -30,6 +32,10 @@ export class Header extends XmlComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get referenceId(): number {
|
||||||
|
return this.refId;
|
||||||
|
}
|
||||||
|
|
||||||
public addParagraph(paragraph: Paragraph): void {
|
public addParagraph(paragraph: Paragraph): void {
|
||||||
this.root.push(paragraph);
|
this.root.push(paragraph);
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,6 @@ export * from "./file";
|
|||||||
export * from "./numbering";
|
export * from "./numbering";
|
||||||
export * from "./media";
|
export * from "./media";
|
||||||
export * from "./drawing";
|
export * from "./drawing";
|
||||||
|
export * from "./document";
|
||||||
export * from "./styles";
|
export * from "./styles";
|
||||||
export * from "./xml-components";
|
export * from "./xml-components";
|
||||||
|
@ -21,13 +21,13 @@ export class Media {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public addMedia(filePath: string, relationshipsCount: number): IMediaData {
|
public addMedia(filePath: string, referenceId: number): IMediaData {
|
||||||
const key = path.basename(filePath);
|
const key = path.basename(filePath);
|
||||||
const dimensions = sizeOf(filePath);
|
const dimensions = sizeOf(filePath);
|
||||||
return this.createMedia(key, relationshipsCount, dimensions, fs.createReadStream(filePath), filePath);
|
return this.createMedia(key, referenceId, dimensions, fs.createReadStream(filePath), filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addMediaWithData(fileName: string, data: Buffer, relationshipsCount: number, width?: number, height?: number): IMediaData {
|
public addMediaWithData(fileName: string, data: Buffer, referenceId: number, width?: number, height?: number): IMediaData {
|
||||||
const key = fileName;
|
const key = fileName;
|
||||||
let dimensions;
|
let dimensions;
|
||||||
if (width && height) {
|
if (width && height) {
|
||||||
@ -39,7 +39,7 @@ export class Media {
|
|||||||
dimensions = sizeOf(data);
|
dimensions = sizeOf(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.createMedia(key, relationshipsCount, dimensions, data);
|
return this.createMedia(key, referenceId, dimensions, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private createMedia(
|
private createMedia(
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
import { Drawing } from "../../drawing";
|
import { Drawing } from "../../drawing";
|
||||||
|
import { IDrawingOptions } from "../../drawing/drawing";
|
||||||
import { IMediaData } from "../../media/data";
|
import { IMediaData } from "../../media/data";
|
||||||
import { Run } from "../run";
|
import { Run } from "../run";
|
||||||
|
|
||||||
export class PictureRun extends Run {
|
export class PictureRun extends Run {
|
||||||
private drawing: Drawing;
|
private drawing: Drawing;
|
||||||
|
|
||||||
constructor(imageData: IMediaData) {
|
constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
if (imageData === undefined) {
|
if (imageData === undefined) {
|
||||||
throw new Error("imageData cannot be undefined");
|
throw new Error("imageData cannot be undefined");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.drawing = new Drawing(imageData);
|
this.drawing = new Drawing(imageData, drawingOptions);
|
||||||
|
|
||||||
this.root.push(this.drawing);
|
this.root.push(this.drawing);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
import * as fastXmlParser from "fast-xml-parser";
|
import * as fastXmlParser from "fast-xml-parser";
|
||||||
|
import { convertToXmlComponent, ImportedRootElementAttributes, ImportedXmlComponent, parseOptions } from "file/xml-components";
|
||||||
import { Styles } from "./";
|
import { Styles } from "./";
|
||||||
import { ImportedRootElementAttributes, ImportedXmlComponent } from "./../../file/xml-components";
|
|
||||||
|
|
||||||
const parseOptions = {
|
|
||||||
ignoreAttributes: false,
|
|
||||||
attributeNamePrefix: "",
|
|
||||||
attrNodeName: "_attr",
|
|
||||||
};
|
|
||||||
|
|
||||||
export class ExternalStylesFactory {
|
export class ExternalStylesFactory {
|
||||||
/**
|
/**
|
||||||
@ -45,20 +38,7 @@ export class ExternalStylesFactory {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// convert the styles one by one
|
// convert the styles one by one
|
||||||
xmlStyles["w:style"].map((style) => this.convertElement("w:style", style)).forEach(importedStyle.push.bind(importedStyle));
|
xmlStyles["w:style"].map((style) => convertToXmlComponent("w:style", style)).forEach(importedStyle.push.bind(importedStyle));
|
||||||
|
|
||||||
return importedStyle;
|
return importedStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tslint:disable-next-line:no-any
|
|
||||||
public convertElement(elementName: string, element: any): ImportedXmlComponent {
|
|
||||||
const xmlElement = new ImportedXmlComponent(elementName, element._attr);
|
|
||||||
if (typeof element === "object") {
|
|
||||||
Object.keys(element)
|
|
||||||
.filter((key) => key !== "_attr")
|
|
||||||
.map((item) => this.convertElement(item, element[item]))
|
|
||||||
.forEach(xmlElement.push.bind(xmlElement));
|
|
||||||
}
|
|
||||||
return xmlElement;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,52 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { ImportedXmlComponent } from "./";
|
import { ImportedXmlComponent, convertToXmlComponent } from "./";
|
||||||
|
|
||||||
|
const xmlString = `
|
||||||
|
<w:p w:one="value 1" w:two="value 2">
|
||||||
|
<w:rPr>
|
||||||
|
<w:noProof>some value</w:noProof>
|
||||||
|
</w:rPr>
|
||||||
|
<w:r active="true">
|
||||||
|
<w:t>Text 1</w:t>
|
||||||
|
</w:r>
|
||||||
|
<w:r active="true">
|
||||||
|
<w:t>Text 2</w:t>
|
||||||
|
</w:r>
|
||||||
|
</w:p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const importedXmlElement = {
|
||||||
|
"w:p": {
|
||||||
|
_attr: { "w:one": "value 1", "w:two": "value 2" },
|
||||||
|
"w:rPr": { "w:noProof": "some value" },
|
||||||
|
"w:r": [{ _attr: { active: "true" }, "w:t": "Text 1" }, { _attr: { active: "true" }, "w:t": "Text 2" }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const convertedXmlElement = {
|
||||||
|
deleted: false,
|
||||||
|
rootKey: "w:p",
|
||||||
|
root: [
|
||||||
|
{
|
||||||
|
deleted: false,
|
||||||
|
rootKey: "w:rPr",
|
||||||
|
root: [{ deleted: false, rootKey: "w:noProof", root: ["some value"] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deleted: false,
|
||||||
|
rootKey: "w:r",
|
||||||
|
root: [{ deleted: false, rootKey: "w:t", root: ["Text 1"] }],
|
||||||
|
_attr: { active: "true" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deleted: false,
|
||||||
|
rootKey: "w:r",
|
||||||
|
root: [{ deleted: false, rootKey: "w:t", root: ["Text 2"] }],
|
||||||
|
_attr: { active: "true" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
_attr: { "w:one": "value 1", "w:two": "value 2" },
|
||||||
|
};
|
||||||
|
|
||||||
describe("ImportedXmlComponent", () => {
|
describe("ImportedXmlComponent", () => {
|
||||||
let importedXmlComponent: ImportedXmlComponent;
|
let importedXmlComponent: ImportedXmlComponent;
|
||||||
@ -31,4 +78,16 @@ describe("ImportedXmlComponent", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should create XmlComponent from xml string", () => {
|
||||||
|
const converted = ImportedXmlComponent.fromXmlString(xmlString);
|
||||||
|
expect(converted).to.eql(convertedXmlElement);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("convertToXmlComponent", () => {
|
||||||
|
it("should convert to xml component", () => {
|
||||||
|
const converted = convertToXmlComponent("w:p", importedXmlElement["w:p"]);
|
||||||
|
expect(converted).to.eql(convertedXmlElement);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,54 @@
|
|||||||
// tslint:disable:no-any
|
/* tslint:disable */
|
||||||
// tslint:disable:variable-name
|
import { XmlComponent, IXmlableObject } from ".";
|
||||||
import { IXmlableObject, XmlComponent } from "./";
|
import * as fastXmlParser from "fast-xml-parser";
|
||||||
|
import { flatMap } from "lodash";
|
||||||
|
|
||||||
|
export const parseOptions = {
|
||||||
|
ignoreAttributes: false,
|
||||||
|
attributeNamePrefix: "",
|
||||||
|
attrNodeName: "_attr",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the given xml element (in json format) into XmlComponent.
|
||||||
|
* Note: If element is array, them it will return ImportedXmlComponent[]. Example for given:
|
||||||
|
* element = [
|
||||||
|
* { w:t: "val 1"},
|
||||||
|
* { w:t: "val 2"}
|
||||||
|
* ]
|
||||||
|
* will return
|
||||||
|
* [
|
||||||
|
* ImportedXmlComponent { rootKey: "w:t", root: [ "val 1" ]},
|
||||||
|
* ImportedXmlComponent { rootKey: "w:t", root: [ "val 2" ]}
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* @param elementName name (rootKey) of the XmlComponent
|
||||||
|
* @param element the xml element in json presentation
|
||||||
|
*/
|
||||||
|
export function convertToXmlComponent(elementName: string, element: any): ImportedXmlComponent | ImportedXmlComponent[] {
|
||||||
|
const xmlElement = new ImportedXmlComponent(elementName, element._attr);
|
||||||
|
if (Array.isArray(element)) {
|
||||||
|
const out: any[] = [];
|
||||||
|
element.forEach((itemInArray) => {
|
||||||
|
out.push(convertToXmlComponent(elementName, itemInArray));
|
||||||
|
});
|
||||||
|
return flatMap(out);
|
||||||
|
} else if (typeof element === "object") {
|
||||||
|
Object.keys(element)
|
||||||
|
.filter((key) => key !== "_attr")
|
||||||
|
.map((item) => convertToXmlComponent(item, element[item]))
|
||||||
|
.forEach((converted) => {
|
||||||
|
if (Array.isArray(converted)) {
|
||||||
|
converted.forEach(xmlElement.push.bind(xmlElement));
|
||||||
|
} else {
|
||||||
|
xmlElement.push(converted);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (element !== "") {
|
||||||
|
xmlElement.push(element);
|
||||||
|
}
|
||||||
|
return xmlElement;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents imported xml component from xml file.
|
* Represents imported xml component from xml file.
|
||||||
@ -8,11 +56,10 @@ import { IXmlableObject, XmlComponent } from "./";
|
|||||||
export class ImportedXmlComponent extends XmlComponent {
|
export class ImportedXmlComponent extends XmlComponent {
|
||||||
private _attr: any;
|
private _attr: any;
|
||||||
|
|
||||||
constructor(rootKey: string, attr?: any) {
|
constructor(rootKey: string, _attr?: any) {
|
||||||
super(rootKey);
|
super(rootKey);
|
||||||
|
if (_attr) {
|
||||||
if (attr) {
|
this._attr = _attr;
|
||||||
this._attr = attr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +89,7 @@ export class ImportedXmlComponent extends XmlComponent {
|
|||||||
* ]
|
* ]
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
public prepForXml(): IXmlableObject {
|
prepForXml(): IXmlableObject {
|
||||||
const result = super.prepForXml();
|
const result = super.prepForXml();
|
||||||
if (!!this._attr) {
|
if (!!this._attr) {
|
||||||
if (!Array.isArray(result[this.rootKey])) {
|
if (!Array.isArray(result[this.rootKey])) {
|
||||||
@ -53,9 +100,26 @@ export class ImportedXmlComponent extends XmlComponent {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public push(xmlComponent: XmlComponent): void {
|
push(xmlComponent: XmlComponent) {
|
||||||
this.root.push(xmlComponent);
|
this.root.push(xmlComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the xml string to a XmlComponent tree.
|
||||||
|
*
|
||||||
|
* @param importedContent xml content of the imported component
|
||||||
|
*/
|
||||||
|
static fromXmlString(importedContent: string): ImportedXmlComponent {
|
||||||
|
const imported = fastXmlParser.parse(importedContent, parseOptions);
|
||||||
|
const elementName = Object.keys(imported)[0];
|
||||||
|
|
||||||
|
const converted = convertToXmlComponent(elementName, imported[elementName]);
|
||||||
|
|
||||||
|
if (Array.isArray(converted) && converted.length > 1) {
|
||||||
|
throw new Error("Invalid conversion, input must be one element.");
|
||||||
|
}
|
||||||
|
return Array.isArray(converted) ? converted[0] : converted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user