Merge pull request #234 from dolanmiu/feat/improve-docs

Make ImportDotx easier to read
This commit is contained in:
Dolan
2019-01-11 01:15:42 +00:00
committed by GitHub
6 changed files with 89 additions and 80 deletions

View File

@ -15,10 +15,22 @@ const item2 = new Paragraph("line with contextual spacing");
const item3 = new Paragraph("line without contextual spacing"); const item3 = new Paragraph("line without contextual spacing");
const item4 = new Paragraph("line without contextual spacing"); const item4 = new Paragraph("line without contextual spacing");
item1.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(true); item1
item2.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(true); .setNumbering(concrete, 0)
item3.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(false); .spacing({ before: 200 })
item4.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(false); .contextualSpacing(true);
item2
.setNumbering(concrete, 0)
.spacing({ before: 200 })
.contextualSpacing(true);
item3
.setNumbering(concrete, 0)
.spacing({ before: 200 })
.contextualSpacing(false);
item4
.setNumbering(concrete, 0)
.spacing({ before: 200 })
.contextualSpacing(false);
doc.addParagraph(item1); doc.addParagraph(item1);
doc.addParagraph(item2); doc.addParagraph(item2);
@ -29,4 +41,4 @@ const packer = new Packer();
packer.toBuffer(doc).then((buffer) => { packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer); fs.writeFileSync("My Document.docx", buffer);
}); });

View File

@ -1,11 +1,12 @@
// Simple example to add text to a document // Simple example to add text to a document
// Import from 'docx' rather than '../build' if you install from npm // Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs"; import * as fs from "fs";
import { Document, Packer, Paragraph, TextRun } from "../build"; import { Document, Packer, Paragraph } from "../build";
const doc = new Document();
const paragraph = new Paragraph();
const link = doc.createHyperlink("http://www.example.com", "Hyperlink");
var doc = new Document();
var paragraph = new Paragraph();
var link = doc.createHyperlink('http://www.example.com', 'Hyperlink');
link.bold(); link.bold();
paragraph.addHyperLink(link); paragraph.addHyperLink(link);
doc.addParagraph(paragraph); doc.addParagraph(paragraph);

View File

@ -53,8 +53,9 @@ export class Document extends XmlComponent {
return para; return para;
} }
public addTable(table: Table): void { public addTable(table: Table): Document {
this.body.push(table); this.body.push(table);
return this;
} }
public createTable(rows: number, cols: number): Table { public createTable(rows: number, cols: number): Table {

View File

@ -58,12 +58,13 @@ export class File {
this.coreProperties = new CoreProperties(options); this.coreProperties = new CoreProperties(options);
this.numbering = new Numbering(); this.numbering = new Numbering();
this.docRelationships = new Relationships(); this.docRelationships = new Relationships();
this.media = new Media();
this.fileRelationships = new Relationships(); this.fileRelationships = new Relationships();
this.appProperties = new AppProperties(); this.appProperties = new AppProperties();
this.footNotes = new FootNotes(); this.footNotes = new FootNotes();
this.contentTypes = new ContentTypes(); this.contentTypes = new ContentTypes();
this.media = fileProperties.template && fileProperties.template.media ? fileProperties.template.media : new Media();
if (fileProperties.template) { if (fileProperties.template) {
this.currentRelationshipId = fileProperties.template.currentRelationshipId + 1; this.currentRelationshipId = fileProperties.template.currentRelationshipId + 1;
} }
@ -73,7 +74,8 @@ export class File {
throw Error("can not use both template and external styles"); throw Error("can not use both template and external styles");
} }
if (fileProperties.template) { if (fileProperties.template) {
this.styles = fileProperties.template.styles; const stylesFactory = new ExternalStylesFactory();
this.styles = stylesFactory.newInstance(fileProperties.template.styles);
} else if (options.externalStyles) { } else if (options.externalStyles) {
const stylesFactory = new ExternalStylesFactory(); const stylesFactory = new ExternalStylesFactory();
this.styles = stylesFactory.newInstance(options.externalStyles); this.styles = stylesFactory.newInstance(options.externalStyles);
@ -110,8 +112,9 @@ export class File {
this.settings = new Settings(); this.settings = new Settings();
} }
public addTableOfContents(toc: TableOfContents): void { public addTableOfContents(toc: TableOfContents): File {
this.document.addTableOfContents(toc); this.document.addTableOfContents(toc);
return this;
} }
public addParagraph(paragraph: Paragraph): File { public addParagraph(paragraph: Paragraph): File {
@ -123,8 +126,9 @@ export class File {
return this.document.createParagraph(text); return this.document.createParagraph(text);
} }
public addTable(table: Table): void { public addTable(table: Table): File {
return this.document.addTable(table); this.document.addTable(table);
return this;
} }
public createTable(rows: number, cols: number): Table { public createTable(rows: number, cols: number): Table {

View File

@ -7,7 +7,7 @@ describe("ImportDotx", () => {
it("should create", () => { it("should create", () => {
const file = new ImportDotx(); const file = new ImportDotx();
expect(file).to.deep.equal({ currentRelationshipId: 1 }); expect(file).to.deep.equal({});
}); });
}); });

View File

@ -7,8 +7,6 @@ import { FooterWrapper, IDocumentFooter } from "file/footer-wrapper";
import { HeaderWrapper, IDocumentHeader } from "file/header-wrapper"; import { HeaderWrapper, IDocumentHeader } from "file/header-wrapper";
import { Media } from "file/media"; import { Media } from "file/media";
import { TargetModeType } from "file/relationships/relationship/relationship"; import { TargetModeType } from "file/relationships/relationship/relationship";
import { Styles } from "file/styles";
import { ExternalStylesFactory } from "file/styles/external-styles-factory";
import { convertToXmlComponent, ImportedXmlComponent } from "file/xml-components"; import { convertToXmlComponent, ImportedXmlComponent } from "file/xml-components";
const schemeToType = { const schemeToType = {
@ -42,37 +40,30 @@ export interface IDocumentTemplate {
readonly currentRelationshipId: number; readonly currentRelationshipId: number;
readonly headers: IDocumentHeader[]; readonly headers: IDocumentHeader[];
readonly footers: IDocumentFooter[]; readonly footers: IDocumentFooter[];
readonly styles: Styles; readonly styles: string;
readonly titlePageIsDefined: boolean; readonly titlePageIsDefined: boolean;
readonly media: Media;
} }
export class ImportDotx { export class ImportDotx {
// tslint:disable-next-line:readonly-keyword
private currentRelationshipId: number;
constructor() {
this.currentRelationshipId = 1;
}
public async extract(data: Buffer): Promise<IDocumentTemplate> { public async extract(data: Buffer): Promise<IDocumentTemplate> {
const zipContent = await JSZip.loadAsync(data); const zipContent = await JSZip.loadAsync(data);
const stylesContent = await zipContent.files["word/styles.xml"].async("text");
const documentContent = await zipContent.files["word/document.xml"].async("text"); const documentContent = await zipContent.files["word/document.xml"].async("text");
const relationshipContent = await zipContent.files["word/_rels/document.xml.rels"].async("text"); const relationshipContent = await zipContent.files["word/_rels/document.xml.rels"].async("text");
const stylesFactory = new ExternalStylesFactory();
const documentRefs = this.extractDocumentRefs(documentContent); const documentRefs = this.extractDocumentRefs(documentContent);
const documentRelationships = this.findReferenceFiles(relationshipContent); const documentRelationships = this.findReferenceFiles(relationshipContent);
const media = new Media(); const media = new Media();
const templateDocument: IDocumentTemplate = { const templateDocument: IDocumentTemplate = {
headers: await this.createHeaders(zipContent, documentRefs, documentRelationships, media), headers: await this.createHeaders(zipContent, documentRefs, documentRelationships, media, 0),
footers: await this.createFooters(zipContent, documentRefs, documentRelationships, media), footers: await this.createFooters(zipContent, documentRefs, documentRelationships, media, documentRefs.headers.length),
currentRelationshipId: this.currentRelationshipId, currentRelationshipId: documentRefs.footers.length + documentRefs.headers.length,
styles: stylesFactory.newInstance(stylesContent), styles: await zipContent.files["word/styles.xml"].async("text"),
titlePageIsDefined: this.checkIfTitlePageIsDefined(documentContent), titlePageIsDefined: this.checkIfTitlePageIsDefined(documentContent),
media: media,
}; };
return templateDocument; return templateDocument;
@ -83,34 +74,34 @@ export class ImportDotx {
documentRefs: IDocumentRefs, documentRefs: IDocumentRefs,
documentRelationships: IRelationshipFileInfo[], documentRelationships: IRelationshipFileInfo[],
media: Media, media: Media,
startingRelationshipId: number,
): Promise<IDocumentFooter[]> { ): Promise<IDocumentFooter[]> {
const footers: IDocumentFooter[] = []; const result = documentRefs.footers
.map(async (reference, i) => {
const relationshipFileInfo = documentRelationships.find((rel) => rel.id === reference.id);
for (const footerRef of documentRefs.footers) { if (relationshipFileInfo === null || !relationshipFileInfo) {
const relationFileInfo = documentRelationships.find((rel) => rel.id === footerRef.id); throw new Error(`Can not find target file for id ${reference.id}`);
if (relationFileInfo === null || !relationFileInfo) {
throw new Error(`Can not find target file for id ${footerRef.id}`);
}
const xmlData = await zipContent.files[`word/${relationFileInfo.target}`].async("text");
const xmlObj = xml2js(xmlData, { compact: false, captureSpacesBetweenElements: true }) as XMLElement;
let footerXmlElement: XMLElement | undefined;
for (const xmlElm of xmlObj.elements || []) {
if (xmlElm.name === "w:ftr") {
footerXmlElement = xmlElm;
} }
}
if (footerXmlElement === undefined) {
continue;
}
const importedComp = convertToXmlComponent(footerXmlElement) as ImportedXmlComponent;
const footer = new FooterWrapper(media, this.currentRelationshipId++, importedComp);
await this.addRelationshipToWrapper(relationFileInfo, zipContent, footer, media);
footers.push({ type: footerRef.type, footer });
}
return footers; const xmlData = await zipContent.files[`word/${relationshipFileInfo.target}`].async("text");
const xmlObj = xml2js(xmlData, { compact: false, captureSpacesBetweenElements: true }) as XMLElement;
if (!xmlObj.elements) {
return undefined;
}
const xmlElement = xmlObj.elements.reduce((acc, current) => (current.name === "w:ftr" ? current : acc));
const importedComp = convertToXmlComponent(xmlElement) as ImportedXmlComponent;
const wrapper = new FooterWrapper(media, startingRelationshipId + i, importedComp);
await this.addRelationshipToWrapper(relationshipFileInfo, zipContent, wrapper, media);
return { type: reference.type, footer: wrapper };
})
.filter((x) => !!x) as Array<Promise<IDocumentFooter>>;
return Promise.all(result);
} }
private async createHeaders( private async createHeaders(
@ -118,34 +109,34 @@ export class ImportDotx {
documentRefs: IDocumentRefs, documentRefs: IDocumentRefs,
documentRelationships: IRelationshipFileInfo[], documentRelationships: IRelationshipFileInfo[],
media: Media, media: Media,
startingRelationshipId: number,
): Promise<IDocumentHeader[]> { ): Promise<IDocumentHeader[]> {
const headers: IDocumentHeader[] = []; const result = documentRefs.headers
.map(async (reference, i) => {
const relationshipFileInfo = documentRelationships.find((rel) => rel.id === reference.id);
for (const headerRef of documentRefs.headers) { if (relationshipFileInfo === null || !relationshipFileInfo) {
const relationFileInfo = documentRelationships.find((rel) => rel.id === headerRef.id); throw new Error(`Can not find target file for id ${reference.id}`);
if (relationFileInfo === null || !relationFileInfo) {
throw new Error(`Can not find target file for id ${headerRef.id}`);
}
const xmlData = await zipContent.files[`word/${relationFileInfo.target}`].async("text");
const xmlObj = xml2js(xmlData, { compact: false, captureSpacesBetweenElements: true }) as XMLElement;
let headerXmlElement: XMLElement | undefined;
for (const xmlElm of xmlObj.elements || []) {
if (xmlElm.name === "w:hdr") {
headerXmlElement = xmlElm;
} }
}
if (headerXmlElement === undefined) {
continue;
}
const importedComp = convertToXmlComponent(headerXmlElement) as ImportedXmlComponent;
const header = new HeaderWrapper(media, this.currentRelationshipId++, importedComp);
// await this.addMedia(zipContent, media, documentRefs, documentRelationships);
await this.addRelationshipToWrapper(relationFileInfo, zipContent, header, media);
headers.push({ type: headerRef.type, header });
}
return headers; const xmlData = await zipContent.files[`word/${relationshipFileInfo.target}`].async("text");
const xmlObj = xml2js(xmlData, { compact: false, captureSpacesBetweenElements: true }) as XMLElement;
if (!xmlObj.elements) {
return undefined;
}
const xmlElement = xmlObj.elements.reduce((acc, current) => (current.name === "w:hdr" ? current : acc));
const importedComp = convertToXmlComponent(xmlElement) as ImportedXmlComponent;
const wrapper = new HeaderWrapper(media, startingRelationshipId + i, importedComp);
await this.addRelationshipToWrapper(relationshipFileInfo, zipContent, wrapper, media);
return { type: reference.type, header: wrapper };
})
.filter((x) => !!x) as Array<Promise<IDocumentHeader>>;
return Promise.all(result);
} }
private async addRelationshipToWrapper( private async addRelationshipToWrapper(