Skip to content

Commit

Permalink
feat: added footer support
Browse files Browse the repository at this point in the history
  • Loading branch information
privateOmega committed Dec 1, 2020
1 parent 2fdab11 commit 24f60df
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 4 deletions.
13 changes: 11 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,28 @@ const minifyHTMLString = (htmlString) => {
}
};

async function generateContainer(htmlString, headerHTMLString, documentOptions = {}) {
async function generateContainer(
htmlString,
headerHTMLString,
documentOptions = {},
footerHTMLString
) {
const zip = new JSZip();

let contentHTML = htmlString;
let headerHTML = headerHTMLString;
let footerHTML = footerHTMLString;
if (htmlString) {
contentHTML = minifyHTMLString(contentHTML);
}
if (headerHTMLString) {
headerHTML = minifyHTMLString(headerHTML);
}
if (footerHTMLString) {
footerHTML = minifyHTMLString(footerHTML);
}

addFilesToContainer(zip, contentHTML, documentOptions, headerHTML);
addFilesToContainer(zip, contentHTML, documentOptions, headerHTML, footerHTML);

const buffer = await zip.generateAsync({ type: 'arraybuffer' });
if (Object.prototype.hasOwnProperty.call(global, 'Blob')) {
Expand Down
81 changes: 81 additions & 0 deletions src/docx-document.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class DocxDocument {
modifiedAt,
headerType,
header,
footerType,
footer,
font,
fontSize,
complexScriptFontSize,
Expand Down Expand Up @@ -82,6 +84,8 @@ class DocxDocument {
this.modifiedAt = modifiedAt || new Date();
this.headerType = headerType || 'default';
this.header = header || false;
this.footerType = footerType || 'default';
this.footer = footer || false;
this.font = font || 'Times New Roman';
this.fontSize = fontSize || 22;
this.complexScriptFontSize = complexScriptFontSize || 22;
Expand All @@ -90,12 +94,14 @@ class DocxDocument {
this.lastNumberingId = 0;
this.lastMediaId = 0;
this.lastHeaderId = 0;
this.lastFooterId = 0;
this.stylesObjects = [];
this.numberingObjects = [];
this.relationshipFilename = 'document';
this.relationships = [{ fileName: 'document', lastRelsId: 4, rels: [] }];
this.mediaFiles = [];
this.headerObjects = [];
this.footerObjects = [];
this.documentXML = null;

this.generateContentTypesXML = this.generateContentTypesXML.bind(this);
Expand All @@ -110,6 +116,7 @@ class DocxDocument {
this.createMediaFile = this.createMediaFile.bind(this);
this.createDocumentRelationships = this.createDocumentRelationships.bind(this);
this.generateHeaderXML = this.generateHeaderXML.bind(this);
this.generateFooterXML = this.generateFooterXML.bind(this);
}

generateContentTypesXML() {
Expand All @@ -135,6 +142,26 @@ class DocxDocument {
}
);
}
if (this.footerObjects && Array.isArray(this.footerObjects) && this.footerObjects.length) {
this.footerObjects.forEach(
// eslint-disable-next-line array-callback-return
({ footerId }) => {
const contentTypesFragment = fragment({
defaultNamespace: {
ele: 'http://schemas.openxmlformats.org/package/2006/content-types',
},
})
.ele('Override')
.att('PartName', `/word/footer${footerId}.xml`)
.att(
'ContentType',
'application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml'
)
.up();
contentTypesXML.root().import(contentTypesFragment);
}
);
}

return contentTypesXML.toString({ prettyPrint: true });
}
Expand Down Expand Up @@ -192,6 +219,33 @@ class DocxDocument {

documentXML.root().first().first().import(headerXmlFragment);
}
if (
this.footer &&
this.footerObjects &&
Array.isArray(this.footerObjects) &&
this.footerObjects.length
) {
const footerXmlFragment = fragment();

this.footerObjects.forEach(
// eslint-disable-next-line array-callback-return
({ relationshipId, type }) => {
const footerFragment = fragment({
namespaceAlias: {
w: namespaces.w,
r: namespaces.r,
},
})
.ele('@w', 'footerReference')
.att('@r', 'id', `rId${relationshipId}`)
.att('@w', 'type', type)
.up();
footerXmlFragment.import(footerFragment);
}
);

documentXML.root().first().first().import(footerXmlFragment);
}

return documentXML.toString({ prettyPrint: true });
}
Expand Down Expand Up @@ -414,6 +468,9 @@ class DocxDocument {
case 'header':
relationshipType = namespaces.headers;
break;
case 'footer':
relationshipType = namespaces.footers;
break;
default:
break;
}
Expand Down Expand Up @@ -451,6 +508,30 @@ class DocxDocument {

return { headerId: this.lastHeaderId, headerXML };
}

generateFooterXML(vTree) {
const footerXML = create({
encoding: 'UTF-8',
standalone: true,
namespaceAlias: {
w: namespaces.w,
ve: namespaces.ve,
o: namespaces.o,
r: namespaces.r,
v: namespaces.v,
wp: namespaces.wp,
w10: namespaces.w10,
},
}).ele('@w', 'ftr');

const XMLFragment = fragment();
convertVTreeToXML(this, vTree, XMLFragment);
footerXML.root().import(XMLFragment);

this.lastFooterId += 1;

return { footerId: this.lastFooterId, footerXML };
}
}

export default DocxDocument;
1 change: 1 addition & 0 deletions src/helpers/namespaces.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const namespaces = {
images: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
styles: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles',
headers: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/header',
footers: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer',
coreProperties: 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties',
officeDocumentRelation:
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument',
Expand Down
32 changes: 30 additions & 2 deletions src/html-to-docx.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const defaultDocumentOptions = {
modifiedAt: new Date(),
headerType: 'default',
header: false,
footerType: 'default',
footer: false,
font: 'Times New Roman',
fontSize: 22,
complexScriptFontSize: 22,
Expand Down Expand Up @@ -120,7 +122,13 @@ const normalizeDocumentOptions = (documentOptions) => {
// Ref: https://en.wikipedia.org/wiki/Office_Open_XML_file_formats
// http://officeopenxml.com/anatomyofOOXML.php
// eslint-disable-next-line import/prefer-default-export
export function addFilesToContainer(zip, htmlString, suppliedDocumentOptions, headerHTMLString) {
export function addFilesToContainer(
zip,
htmlString,
suppliedDocumentOptions,
headerHTMLString,
footerHTMLString
) {
const normalizedDocumentOptions = normalizeDocumentOptions(suppliedDocumentOptions);
const documentOptions = mergeOptions(defaultDocumentOptions, normalizedDocumentOptions);

Expand Down Expand Up @@ -158,7 +166,27 @@ export function addFilesToContainer(zip, htmlString, suppliedDocumentOptions, he
createFolders: false,
});

docxDocument.headerObjects.push({ headerId, relationshipId, type: 'default' });
docxDocument.headerObjects.push({ headerId, relationshipId, type: docxDocument.headerType });
}
if (docxDocument.footer && footerHTMLString) {
const vTree = convertHTML(footerHTMLString);

docxDocument.relationshipFilename = 'footer1';
const { footerId, footerXML } = docxDocument.generateFooterXML(vTree);
docxDocument.relationshipFilename = 'document';

const relationshipId = docxDocument.createDocumentRelationships(
docxDocument.relationshipFilename,
'footer',
`footer${footerId}.xml`,
'Internal'
);

zip.folder('word').file(`footer${footerId}.xml`, footerXML.toString({ prettyPrint: true }), {
createFolders: false,
});

docxDocument.footerObjects.push({ footerId, relationshipId, type: docxDocument.footerType });
}

zip
Expand Down

0 comments on commit 24f60df

Please sign in to comment.