Compare commits
100 Commits
Author | SHA1 | Date | |
---|---|---|---|
ba0ca78696 | |||
22ba53ad4c | |||
81304f50d1 | |||
3c22372f49 | |||
7584671312 | |||
7296c9e744 | |||
c63a8982e4 | |||
8d35dc1bb0 | |||
0fedfcf709 | |||
6c2eb882bb | |||
59560d96ba | |||
864c9afd93 | |||
b4f07f51ae | |||
e9b153095c | |||
7968c1efcf | |||
87648a742c | |||
4d7bdc2ed9 | |||
d10c707f12 | |||
ac512b2eab | |||
fdf6a59c4c | |||
4b9a6a6735 | |||
49cc8a267c | |||
68cb57aea6 | |||
9d7fd55e4c | |||
195c62f80b | |||
1fd222abea | |||
ac40e13e33 | |||
53ab822dbc | |||
0c9c292291 | |||
573dd753a7 | |||
79b5b3a1f6 | |||
52e8fe576e | |||
0d34d2d92e | |||
b389ac6347 | |||
534c601068 | |||
424436579b | |||
a716360faa | |||
af485c678d | |||
84e298e7ee | |||
753287d9d1 | |||
21bb8f9016 | |||
dc136daeab | |||
e67f5f80e1 | |||
3691d79a4a | |||
8108eca2fa | |||
4f48c8fb80 | |||
20ba081308 | |||
2119ae769b | |||
c618ca7539 | |||
8b11140be2 | |||
bebfec7755 | |||
124aac4888 | |||
b3bfd063d8 | |||
c92cab5e5b | |||
8c613195f3 | |||
41f941728e | |||
ecf1542d95 | |||
06b2bbba25 | |||
0494fdeabd | |||
226206b100 | |||
02f80bc616 | |||
3189c9251a | |||
ae43137906 | |||
50bee30799 | |||
38484a3063 | |||
919327ed08 | |||
c00c5fa02d | |||
80f09ac10b | |||
ee721ffbec | |||
323f91dd68 | |||
810ccb40d7 | |||
5242f7d55c | |||
d293ae516c | |||
3fb563f9c8 | |||
1dd1c65341 | |||
e593327fea | |||
64e0aeeb18 | |||
05816abc12 | |||
ce306aef07 | |||
373c850f35 | |||
a0e00b8eff | |||
a954c69458 | |||
a0e034bd55 | |||
1a1c1f26d9 | |||
114c429aed | |||
40251a76f6 | |||
a102b479e6 | |||
5b5f5ea203 | |||
40a8e581f1 | |||
1b988e7135 | |||
9433c7aedc | |||
479dfed987 | |||
1866033128 | |||
844b9ab2ec | |||
e6713d6ce2 | |||
069ce73c07 | |||
6dda6a5513 | |||
329def42ac | |||
c5137b5fad | |||
ab05ccb846 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -46,3 +46,6 @@ build-tests
|
|||||||
|
|
||||||
# Lock files
|
# Lock files
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
|
# Documents
|
||||||
|
My Document.docx
|
||||||
|
5
.prettierrc.yml
Normal file
5
.prettierrc.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
trailingComma: all
|
||||||
|
printWidth: 140
|
||||||
|
tabWidth: 4
|
||||||
|
arrowParens: always
|
||||||
|
bracketSpacing: true
|
20
.travis.yml
20
.travis.yml
@ -15,11 +15,21 @@ script:
|
|||||||
- node ./demo/demo5.js
|
- node ./demo/demo5.js
|
||||||
- node ./demo/demo6.js
|
- node ./demo/demo6.js
|
||||||
- node ./demo/demo7.js
|
- node ./demo/demo7.js
|
||||||
|
- node ./demo/demo8.js
|
||||||
|
- node ./demo/demo9.js
|
||||||
|
- node ./demo/demo10.js
|
||||||
|
- node ./demo/demo11.js
|
||||||
|
- node ./demo/demo12.js
|
||||||
after_failure:
|
after_failure:
|
||||||
- "cat /home/travis/builds/dolanmiu/docx/npm-debug.log"
|
- "cat /home/travis/builds/dolanmiu/docx/npm-debug.log"
|
||||||
after_success:
|
after_success:
|
||||||
- bash ./deploy-docs.sh
|
- npm run typedoc
|
||||||
env:
|
- echo "janchi.co.uk" > docs/.nojekyll
|
||||||
global:
|
deploy:
|
||||||
- ENCRYPTION_LABEL: "ad385fa3b525"
|
provider: pages
|
||||||
|
skip-cleanup: true
|
||||||
|
github-token: $GITHUB_TOKEN
|
||||||
|
keep-history: true
|
||||||
|
local-dir: docs
|
||||||
|
on:
|
||||||
|
branch: master
|
||||||
|
45
README.md
45
README.md
@ -6,16 +6,16 @@
|
|||||||
Generate .docx files with JS/TS very easily, written in TS.
|
Generate .docx files with JS/TS very easily, written in TS.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
-----
|
---
|
||||||
|
|
||||||
[![NPM version][npm-image]][npm-url]
|
[![NPM version][npm-image]][npm-url]
|
||||||
|
[![Downloads per month][downloads-image]][downloads-url]
|
||||||
[![Build Status][travis-image]][travis-url]
|
[![Build Status][travis-image]][travis-url]
|
||||||
[![Dependency Status][gemnasium-image]][gemnasium-url]
|
[![Dependency Status][daviddm-image]][daviddm-url]
|
||||||
[![Known Vulnerabilities][snky-image]][snky-url]
|
[![Known Vulnerabilities][snky-image]][snky-url]
|
||||||
[![Chat on Gitter][gitter-image]][gitter-url]
|
[![Chat on Gitter][gitter-image]][gitter-url]
|
||||||
[![code style: prettier][prettier-image]][prettier-url]
|
[![code style: prettier][prettier-image]][prettier-url]
|
||||||
|
[![PRs Welcome][pr-image]][pr-url]
|
||||||
[](https://nodei.co/npm/docx/)
|
|
||||||
|
|
||||||
# docx
|
# docx
|
||||||
|
|
||||||
@ -27,23 +27,39 @@ $ npm install --save docx
|
|||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
|
Press `endpoint` on the `RunKit` website:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo1 - Simple paragraph and text
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo2 - Advanced Paragraphs and text
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo3 - Bullet points
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo4 - Simple table
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo5 - Images
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo6 - Margins
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo7 - Landscape
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo8/1.0.1 - Header and Footer
|
||||||
|
* https://runkit.com/dolanmiu/docx-demo10 - **My CV generated with docx**
|
||||||
|
|
||||||
|
#### Run demos locally:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ npm run demo
|
$ npm run demo
|
||||||
```
|
```
|
||||||
|
|
||||||
will run the demo selector app in the `demo` folder. It will prompt you to select a demo number, which will run a demo from that folder.
|
This command will run the demo selector app in the `demo` folder. It will prompt you to select a demo number, which will run a demo from that folder.
|
||||||
|
|
||||||
## Guide
|
## Guide
|
||||||
|
|
||||||
Please refer to [the Wiki](https://github.com/dolanmiu/docx/wiki) for details on how to use this library, examples and much more!
|
Please refer to [the Wiki](https://github.com/dolanmiu/docx/wiki) for details on how to use this library, examples and much more!
|
||||||
|
|
||||||
Full documentation can be found here: [http://dolanmiu.github.io/docx](http://dolanmiu.github.io/docx)
|
Full documentation can be found here: [http://dolanmiu.github.io/docx/index.html](http://dolanmiu.github.io/docx/index.html)
|
||||||
|
|
||||||
## Simple Usage
|
## Simple Usage
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Used to create docx files
|
// Used to create docx files
|
||||||
var docx = require('docx');
|
var docx = require("docx");
|
||||||
|
|
||||||
// Create document
|
// Create document
|
||||||
var doc = new docx.Document();
|
var doc = new docx.Document();
|
||||||
@ -51,7 +67,7 @@ var doc = new docx.Document();
|
|||||||
// Add some content in the document
|
// Add some content in the document
|
||||||
var paragraph = new docx.Paragraph("Some cool text here.");
|
var paragraph = new docx.Paragraph("Some cool text here.");
|
||||||
// Add more text into the paragraph if you wish
|
// Add more text into the paragraph if you wish
|
||||||
paragraph.addRun(new docx.TextRun('Lorem Ipsum Foo Bar'));
|
paragraph.addRun(new docx.TextRun("Lorem Ipsum Foo Bar"));
|
||||||
doc.addParagraph(paragraph);
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
// Used to export the file into a .docx file
|
// Used to export the file into a .docx file
|
||||||
@ -61,9 +77,9 @@ var exporter = new docx.LocalPacker(doc);
|
|||||||
// res is express' Response object
|
// res is express' Response object
|
||||||
var exporter = new docx.ExpressPacker(doc, res);
|
var exporter = new docx.ExpressPacker(doc, res);
|
||||||
|
|
||||||
exporter.pack('My First Document');
|
exporter.pack("My First Document");
|
||||||
// If you want to export it as a .pdf file instead
|
// If you want to export it as a .pdf file instead
|
||||||
exporter.packPdf('My First Document');
|
exporter.packPdf("My First Document");
|
||||||
|
|
||||||
// done! A file called 'My First Document.docx'
|
// done! A file called 'My First Document.docx'
|
||||||
// will be in your file system if you used LocalPacker
|
// will be in your file system if you used LocalPacker
|
||||||
@ -71,13 +87,14 @@ 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) 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).
|
||||||
|
|
||||||
-----
|
---
|
||||||
|
|
||||||
Made with 💖
|
Made with 💖
|
||||||
|
|
||||||
@ -85,6 +102,8 @@ Huge thanks to [@felipeochoa](https://github.com/felipeochoa) for awesome contri
|
|||||||
|
|
||||||
[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-url]: https://npmjs.org/package/docx
|
||||||
[travis-image]: https://travis-ci.org/dolanmiu/docx.svg?branch=master
|
[travis-image]: https://travis-ci.org/dolanmiu/docx.svg?branch=master
|
||||||
[travis-url]: https://travis-ci.org/dolanmiu/docx
|
[travis-url]: https://travis-ci.org/dolanmiu/docx
|
||||||
[daviddm-image]: https://david-dm.org/dolanmiu/docx.svg?theme=shields.io
|
[daviddm-image]: https://david-dm.org/dolanmiu/docx.svg?theme=shields.io
|
||||||
@ -93,7 +112,7 @@ Huge thanks to [@felipeochoa](https://github.com/felipeochoa) for awesome contri
|
|||||||
[snky-url]: https://snyk.io/test/github/dolanmiu/docx
|
[snky-url]: https://snyk.io/test/github/dolanmiu/docx
|
||||||
[gitter-image]: https://badges.gitter.im/dolanmiu/docx.svg
|
[gitter-image]: https://badges.gitter.im/dolanmiu/docx.svg
|
||||||
[gitter-url]: https://gitter.im/docx-lib/Lobby
|
[gitter-url]: https://gitter.im/docx-lib/Lobby
|
||||||
[gemnasium-image]: https://gemnasium.com/badges/github.com/dolanmiu/docx.svg
|
|
||||||
[gemnasium-url]: https://gemnasium.com/github.com/dolanmiu/docx
|
|
||||||
[prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg
|
[prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg
|
||||||
[prettier-url]: https://github.com/prettier/prettier
|
[prettier-url]: https://github.com/prettier/prettier
|
||||||
|
[pr-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
|
||||||
|
[pr-url]: http://makeapullrequest.com
|
||||||
|
2
demo/assets/custom-styles.xml
Normal file
2
demo/assets/custom-styles.xml
Normal file
File diff suppressed because one or more lines are too long
314
demo/demo10.js
Normal file
314
demo/demo10.js
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
const docx = require("../build");
|
||||||
|
|
||||||
|
const PHONE_NUMBER = "07534563401";
|
||||||
|
const PROFILE_URL = "https://www.linkedin.com/in/dolan1";
|
||||||
|
const EMAIL = "docx@docx.com";
|
||||||
|
|
||||||
|
const experiences = [
|
||||||
|
{
|
||||||
|
isCurrent: true,
|
||||||
|
summary: "Full-stack developer working with Angular and Java. Working for the iShares platform",
|
||||||
|
title: "Associate Software Developer",
|
||||||
|
startDate: {
|
||||||
|
month: 11,
|
||||||
|
year: 2017,
|
||||||
|
},
|
||||||
|
company: {
|
||||||
|
name: "BlackRock",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCurrent: false,
|
||||||
|
summary:
|
||||||
|
"Full-stack developer working with Angular, Node and TypeScript. Working for the iShares platform. Emphasis on Dev-ops and developing the continous integration pipeline.",
|
||||||
|
title: "Software Developer",
|
||||||
|
endDate: {
|
||||||
|
month: 11,
|
||||||
|
year: 2017,
|
||||||
|
},
|
||||||
|
startDate: {
|
||||||
|
month: 10,
|
||||||
|
year: 2016,
|
||||||
|
},
|
||||||
|
company: {
|
||||||
|
name: "Torch Markets",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCurrent: false,
|
||||||
|
summary:
|
||||||
|
"Used ASP.NET MVC 5 to produce a diversity data collection tool for the future of British television.\n\nUsed AngularJS and C# best practices. Technologies used include JavaScript, ASP.NET MVC 5, SQL, Oracle, SASS, Bootstrap, Grunt.",
|
||||||
|
title: "Software Developer",
|
||||||
|
endDate: {
|
||||||
|
month: 10,
|
||||||
|
year: 2016,
|
||||||
|
},
|
||||||
|
startDate: {
|
||||||
|
month: 3,
|
||||||
|
year: 2015,
|
||||||
|
},
|
||||||
|
company: {
|
||||||
|
name: "Soundmouse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isCurrent: false,
|
||||||
|
summary:
|
||||||
|
"Develop web commerce platforms for various high profile clients.\n\nCreated a log analysis web application with the Play Framework in Java, incorporating Test Driven Development. It asynchronously uploads and processes large (2 GB) log files, and outputs meaningful results in context with the problem. \n\nAnalysis and development of the payment system infrastructure and user accounts section to be used by several clients of the company such as Waitrose, Tally Weijl, DJ Sports, Debenhams, Ann Summers, John Lewis and others.\n\nTechnologies used include WebSphere Commerce, Java, JavaScript and JSP.",
|
||||||
|
title: "Java Developer",
|
||||||
|
endDate: {
|
||||||
|
month: 10,
|
||||||
|
year: 2014,
|
||||||
|
},
|
||||||
|
startDate: {
|
||||||
|
month: 3,
|
||||||
|
year: 2013,
|
||||||
|
},
|
||||||
|
company: {
|
||||||
|
name: "Soundmouse",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const education = [
|
||||||
|
{
|
||||||
|
degree: "Master of Science (MSc)",
|
||||||
|
fieldOfStudy: "Computer Science",
|
||||||
|
notes:
|
||||||
|
"Exam Results: 1st Class with Distinction, Dissertation: 1st Class with Distinction\n\nRelevant Courses: Java and C# Programming, Software Engineering, Artificial Intelligence, \nComputational Photography, Algorithmics, Architecture and Hardware.\n\nCreated a Windows 8 game in JavaScript for the dissertation. \n\nCreated an award-winning 3D stereoscopic game in C# using XNA.",
|
||||||
|
schoolName: "University College London",
|
||||||
|
startDate: {
|
||||||
|
year: 2012,
|
||||||
|
},
|
||||||
|
endDate: {
|
||||||
|
year: 2013,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
degree: "Bachelor of Engineering (BEng)",
|
||||||
|
fieldOfStudy: "Material Science and Engineering",
|
||||||
|
notes:
|
||||||
|
"Exam Results: 2:1, Dissertation: 1st Class with Distinction\n\nRelevant courses: C Programming, Mathematics and Business for Engineers.",
|
||||||
|
schoolName: "Imperial College London",
|
||||||
|
startDate: {
|
||||||
|
year: 2009,
|
||||||
|
},
|
||||||
|
endDate: {
|
||||||
|
year: 2012,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const skills = [
|
||||||
|
{
|
||||||
|
name: "Angular",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "TypeScript",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "JavaScript",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NodeJS",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const achievements = [
|
||||||
|
{
|
||||||
|
issuer: "Oracle",
|
||||||
|
name: "Oracle Certified Expert",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
class DocumentCreator {
|
||||||
|
create(data) {
|
||||||
|
const experiences = data[0];
|
||||||
|
const educations = data[1];
|
||||||
|
const skills = data[2];
|
||||||
|
const achivements = data[3];
|
||||||
|
const document = new docx.Document();
|
||||||
|
document.addParagraph(new docx.Paragraph("Dolan Miu").title());
|
||||||
|
|
||||||
|
document.addParagraph(this.createContactInfo(PHONE_NUMBER, PROFILE_URL, EMAIL));
|
||||||
|
document.addParagraph(this.createHeading("Education"));
|
||||||
|
|
||||||
|
for (const education of educations) {
|
||||||
|
document.addParagraph(
|
||||||
|
this.createInstitutionHeader(education.schoolName, `${education.startDate.year} - ${education.endDate.year}`),
|
||||||
|
);
|
||||||
|
document.addParagraph(this.createRoleText(`${education.fieldOfStudy} - ${education.degree}`));
|
||||||
|
|
||||||
|
const bulletPoints = this.splitParagraphIntoBullets(education.notes);
|
||||||
|
bulletPoints.forEach((bulletPoint) => {
|
||||||
|
document.addParagraph(this.createBullet(bulletPoint));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addParagraph(this.createHeading("Experience"));
|
||||||
|
|
||||||
|
for (const position of experiences) {
|
||||||
|
document.addParagraph(
|
||||||
|
this.createInstitutionHeader(
|
||||||
|
position.company.name,
|
||||||
|
this.createPositionDateText(position.startDate, position.endDate, position.isCurrent),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
document.addParagraph(this.createRoleText(position.title));
|
||||||
|
|
||||||
|
const bulletPoints = this.splitParagraphIntoBullets(position.summary);
|
||||||
|
|
||||||
|
bulletPoints.forEach((bulletPoint) => {
|
||||||
|
document.addParagraph(this.createBullet(bulletPoint));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addParagraph(this.createHeading("Skills, Achievements and Interests"));
|
||||||
|
|
||||||
|
document.addParagraph(this.createSubHeading("Skills"));
|
||||||
|
document.addParagraph(this.createSkillList(skills));
|
||||||
|
|
||||||
|
document.addParagraph(this.createSubHeading("Achievements"));
|
||||||
|
|
||||||
|
for (const achievementParagraph of this.createAchivementsList(achivements)) {
|
||||||
|
document.addParagraph(achievementParagraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addParagraph(this.createSubHeading("Interests"));
|
||||||
|
|
||||||
|
document.addParagraph(this.createInterests("Programming, Technology, Music Production, Web Design, 3D Modelling, Dancing."));
|
||||||
|
|
||||||
|
document.addParagraph(this.createHeading("References"));
|
||||||
|
|
||||||
|
document.addParagraph(
|
||||||
|
new docx.Paragraph(
|
||||||
|
"Dr. Dean Mohamedally Director of Postgraduate Studies Department of Computer Science, University College London Malet Place, Bloomsbury, London WC1E d.mohamedally@ucl.ac.uk",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
document.addParagraph(new docx.Paragraph("More references upon request"));
|
||||||
|
document.addParagraph(
|
||||||
|
new docx.Paragraph(
|
||||||
|
"This CV was generated in real-time based on my Linked-In profile from my personal website www.dolan.bio.",
|
||||||
|
).center(),
|
||||||
|
);
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
createContactInfo(phoneNumber, profileUrl, email) {
|
||||||
|
const paragraph = new docx.Paragraph().center();
|
||||||
|
const contactInfo = new docx.TextRun(`Mobile: ${phoneNumber} | LinkedIn: ${profileUrl} | Email: ${email}`);
|
||||||
|
const address = new docx.TextRun("Address: 58 Elm Avenue, Kent ME4 6ER, UK").break();
|
||||||
|
|
||||||
|
paragraph.addRun(contactInfo);
|
||||||
|
paragraph.addRun(address);
|
||||||
|
|
||||||
|
return paragraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
createHeading(text) {
|
||||||
|
return new docx.Paragraph(text).heading1().thematicBreak();
|
||||||
|
}
|
||||||
|
|
||||||
|
createSubHeading(text) {
|
||||||
|
return new docx.Paragraph(text).heading2();
|
||||||
|
}
|
||||||
|
|
||||||
|
createInstitutionHeader(institutionName, dateText) {
|
||||||
|
const paragraph = new docx.Paragraph().maxRightTabStop();
|
||||||
|
const institution = new docx.TextRun(institutionName).bold();
|
||||||
|
const date = new docx.TextRun(dateText).tab().bold();
|
||||||
|
|
||||||
|
paragraph.addRun(institution);
|
||||||
|
paragraph.addRun(date);
|
||||||
|
|
||||||
|
return paragraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
createRoleText(roleText) {
|
||||||
|
const paragraph = new docx.Paragraph();
|
||||||
|
const role = new docx.TextRun(roleText).italic();
|
||||||
|
|
||||||
|
paragraph.addRun(role);
|
||||||
|
|
||||||
|
return paragraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
createBullet(text) {
|
||||||
|
return new docx.Paragraph(text).bullet();
|
||||||
|
}
|
||||||
|
|
||||||
|
createSkillList(skills) {
|
||||||
|
const paragraph = new docx.Paragraph();
|
||||||
|
const skillConcat = skills.map((skill) => skill.name).join(", ") + ".";
|
||||||
|
|
||||||
|
paragraph.addRun(new docx.TextRun(skillConcat));
|
||||||
|
|
||||||
|
return paragraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
createAchivementsList(achivements) {
|
||||||
|
const arr = [];
|
||||||
|
|
||||||
|
for (const achievement of achivements) {
|
||||||
|
arr.push(new docx.Paragraph(achievement.name).bullet());
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
createInterests(interests) {
|
||||||
|
const paragraph = new docx.Paragraph();
|
||||||
|
|
||||||
|
paragraph.addRun(new docx.TextRun(interests));
|
||||||
|
return paragraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
splitParagraphIntoBullets(text) {
|
||||||
|
return text.split("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
createPositionDateText(startDate, endDate, isCurrent) {
|
||||||
|
const startDateText = this.getMonthFromInt(startDate.month) + ". " + startDate.year;
|
||||||
|
const endDateText = isCurrent ? "Present" : `${this.getMonthFromInt(endDate.month)}. ${endDate.year}`;
|
||||||
|
|
||||||
|
return `${startDateText} - ${endDateText}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMonthFromInt(value) {
|
||||||
|
switch (value) {
|
||||||
|
case 1:
|
||||||
|
return "Jan";
|
||||||
|
case 2:
|
||||||
|
return "Feb";
|
||||||
|
case 3:
|
||||||
|
return "Mar";
|
||||||
|
case 4:
|
||||||
|
return "Apr";
|
||||||
|
case 5:
|
||||||
|
return "May";
|
||||||
|
case 6:
|
||||||
|
return "Jun";
|
||||||
|
case 7:
|
||||||
|
return "Jul";
|
||||||
|
case 8:
|
||||||
|
return "Aug";
|
||||||
|
case 9:
|
||||||
|
return "Sept";
|
||||||
|
case 10:
|
||||||
|
return "Oct";
|
||||||
|
case 11:
|
||||||
|
return "Nov";
|
||||||
|
case 12:
|
||||||
|
return "Dec";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const documentCreator = new DocumentCreator();
|
||||||
|
|
||||||
|
const doc = documentCreator.create([experiences, education, skills, achievements]);
|
||||||
|
|
||||||
|
var exporter = new docx.LocalPacker(doc);
|
||||||
|
exporter.pack("Dolan Miu CV");
|
||||||
|
|
||||||
|
console.log("Document created successfully at project root!");
|
132
demo/demo11.js
Normal file
132
demo/demo11.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
const docx = require("../build");
|
||||||
|
|
||||||
|
const doc = new docx.Document(undefined, {
|
||||||
|
top: 700,
|
||||||
|
right: 700,
|
||||||
|
bottom: 700,
|
||||||
|
left: 700,
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("Heading1", "Heading 1")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.quickFormat()
|
||||||
|
.font("Calibri")
|
||||||
|
.size(52)
|
||||||
|
.center()
|
||||||
|
.bold()
|
||||||
|
.color(000000)
|
||||||
|
.spacing({ line: 340 })
|
||||||
|
.underline("single", "000000");
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("Heading2", "Heading 2")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.font("Calibri")
|
||||||
|
.quickFormat()
|
||||||
|
.size(26)
|
||||||
|
.bold()
|
||||||
|
.spacing({ line: 340 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("Heading3", "Heading 3")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.font("Calibri")
|
||||||
|
.quickFormat()
|
||||||
|
.size(26)
|
||||||
|
.bold()
|
||||||
|
.spacing({ line: 276 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("Heading4", "Heading 4")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.justified()
|
||||||
|
.font("Calibri")
|
||||||
|
.size(26)
|
||||||
|
.bold();
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("normalPara", "Normal Para")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.font("Calibri")
|
||||||
|
.quickFormat()
|
||||||
|
.leftTabStop(453.543307087)
|
||||||
|
.maxRightTabStop(453.543307087)
|
||||||
|
.size(26)
|
||||||
|
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("normalPara2", "Normal Para2")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.quickFormat()
|
||||||
|
.font("Calibri")
|
||||||
|
.size(26)
|
||||||
|
.justified()
|
||||||
|
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("aside", "Aside")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.next("Normal")
|
||||||
|
.color("999999")
|
||||||
|
.italics()
|
||||||
|
.indent(720)
|
||||||
|
.spacing({ line: 276 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("wellSpaced", "Well Spaced")
|
||||||
|
.basedOn("Normal")
|
||||||
|
.spacing({ line: 276, before: 20 * 72 * 0.1, after: 20 * 72 * 0.05 });
|
||||||
|
|
||||||
|
doc.Styles.createParagraphStyle("ListParagraph", "List Paragraph")
|
||||||
|
.quickFormat()
|
||||||
|
.basedOn("Normal");
|
||||||
|
|
||||||
|
doc.createImage("./demo/images/pizza.gif");
|
||||||
|
doc
|
||||||
|
.createParagraph("HEADING")
|
||||||
|
.heading1()
|
||||||
|
.center();
|
||||||
|
|
||||||
|
doc.Footer.createParagraph("1")
|
||||||
|
.style("normalPara")
|
||||||
|
.right();
|
||||||
|
|
||||||
|
doc.createParagraph("Ref. :").style("normalPara");
|
||||||
|
doc.createParagraph("Date :").style("normalPara");
|
||||||
|
|
||||||
|
doc.createParagraph("To,").style("normalPara");
|
||||||
|
doc.createParagraph("The Superindenting Engineer,(O &M)").style("normalPara");
|
||||||
|
|
||||||
|
doc.createParagraph("Sub : ").style("normalPara");
|
||||||
|
|
||||||
|
doc.createParagraph("Ref. : ").style("normalPara");
|
||||||
|
|
||||||
|
doc.createParagraph("Sir,").style("normalPara");
|
||||||
|
|
||||||
|
doc.createParagraph("BRIEF DESCRIPTION").style("normalPara");
|
||||||
|
|
||||||
|
var table = new docx.Table(4, 4);
|
||||||
|
var contentParagraph = table
|
||||||
|
.getRow(0)
|
||||||
|
.getCell(0)
|
||||||
|
.addContent(new docx.Paragraph("Pole No."));
|
||||||
|
table.properties.width = 10000;
|
||||||
|
doc.addTable(table);
|
||||||
|
|
||||||
|
var arrboth = [{
|
||||||
|
image: "./demo/images/pizza.gif",
|
||||||
|
comment: "Test"
|
||||||
|
}, {
|
||||||
|
image: "./demo/images/pizza.gif",
|
||||||
|
comment: "Test 2"
|
||||||
|
}];
|
||||||
|
|
||||||
|
arrboth.forEach(function(item) {
|
||||||
|
const para = doc.createParagraph();
|
||||||
|
para.createTextRun(doc.createImage(item.image));
|
||||||
|
para.properties.width = 60;
|
||||||
|
para.properties.height = 90;
|
||||||
|
doc.createParagraph(item.comment).style("normalPara2");
|
||||||
|
});
|
||||||
|
|
||||||
|
var exporter = new docx.LocalPacker(doc);
|
||||||
|
exporter.pack("My Document");
|
21
demo/demo12.js
Normal file
21
demo/demo12.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
const docx = require("../build");
|
||||||
|
|
||||||
|
var doc = new docx.Document();
|
||||||
|
|
||||||
|
var paragraph = new docx.Paragraph("Hello World");
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
const image = doc.createImage("./demo/images/pizza.gif");
|
||||||
|
const image2 = doc.createImage("./demo/images/pizza.gif");
|
||||||
|
const image3 = doc.createImage("./demo/images/pizza.gif");
|
||||||
|
const image4 = doc.createImage("./demo/images/pizza.gif");
|
||||||
|
|
||||||
|
image.scale(0.5);
|
||||||
|
image2.scale(1)
|
||||||
|
image3.scale(2.5);
|
||||||
|
image4.scale(4);
|
||||||
|
|
||||||
|
var exporter = new docx.LocalPacker(doc);
|
||||||
|
exporter.pack("My Document");
|
||||||
|
|
||||||
|
console.log("Document created successfully at project root!");
|
26
demo/demo13.js
Normal file
26
demo/demo13.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// This example shows 3 styles
|
||||||
|
const fs = require('fs');
|
||||||
|
const docx = require('../build');
|
||||||
|
|
||||||
|
const styles = fs.readFileSync('./demo/assets/custom-styles.xml', 'utf-8');
|
||||||
|
const doc = new docx.Document({
|
||||||
|
title: 'Title',
|
||||||
|
externalStyles: styles
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.createParagraph('Cool Heading Text').heading1();
|
||||||
|
|
||||||
|
let paragraph = new docx.Paragraph('This is a custom named style from the template "MyFancyStyle"');
|
||||||
|
paragraph.style('MyFancyStyle');
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
doc.createParagraph('Some normal text')
|
||||||
|
|
||||||
|
doc.createParagraph('MyFancyStyle again').style('MyFancyStyle');
|
||||||
|
paragraph.style('MyFancyStyle');
|
||||||
|
doc.addParagraph(paragraph);
|
||||||
|
|
||||||
|
var exporter = new docx.LocalPacker(doc);
|
||||||
|
exporter.pack('My Document');
|
||||||
|
|
||||||
|
console.log('Document created successfully at project root!');
|
23
demo/demo14.js
Normal file
23
demo/demo14.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
const docx = require('../build');
|
||||||
|
|
||||||
|
var doc = new docx.Document(undefined,{differentFirstPageHeader:true});
|
||||||
|
|
||||||
|
doc.createParagraph("First Page").pageBreak()
|
||||||
|
doc.createParagraph("Second Page");
|
||||||
|
|
||||||
|
var pageNumber = new docx.TextRun().pageNumber()
|
||||||
|
|
||||||
|
var pageoneheader = new docx.Paragraph("First Page Header ").right();
|
||||||
|
|
||||||
|
pageoneheader.addRun(pageNumber);
|
||||||
|
doc.firstPageHeader.addParagraph(pageoneheader);
|
||||||
|
|
||||||
|
var pagetwoheader = new docx.Paragraph("My Title ").right();
|
||||||
|
|
||||||
|
pagetwoheader.addRun(pageNumber)
|
||||||
|
doc.Header.addParagraph(pagetwoheader)
|
||||||
|
|
||||||
|
var exporter = new docx.LocalPacker(doc);
|
||||||
|
exporter.pack('My Document');
|
||||||
|
|
||||||
|
console.log('Document created successfully at project root!');
|
@ -29,6 +29,16 @@ doc.addParagraph(subP);
|
|||||||
doc.addParagraph(secondSubP);
|
doc.addParagraph(secondSubP);
|
||||||
doc.addParagraph(subSubP);
|
doc.addParagraph(subSubP);
|
||||||
|
|
||||||
|
var bullet1 = new docx.Paragraph("Hey you").bullet();
|
||||||
|
var bullet2 = new docx.Paragraph("What's up fam").bullet(1);
|
||||||
|
var bullet3 = new docx.Paragraph("Hello World 2").bullet(2);
|
||||||
|
var bullet4 = new docx.Paragraph("Yeah boi").bullet(3);
|
||||||
|
|
||||||
|
doc.addParagraph(bullet1);
|
||||||
|
doc.addParagraph(bullet2);
|
||||||
|
doc.addParagraph(bullet3);
|
||||||
|
doc.addParagraph(bullet4);
|
||||||
|
|
||||||
var exporter = new docx.LocalPacker(doc);
|
var exporter = new docx.LocalPacker(doc);
|
||||||
exporter.pack('My Document');
|
exporter.pack('My Document');
|
||||||
|
|
||||||
|
@ -10,4 +10,4 @@ doc.Footer.createParagraph("Footer text");
|
|||||||
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!');
|
@ -1,68 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e # Exit with nonzero exit code if anything fails
|
|
||||||
|
|
||||||
SOURCE_BRANCH="master"
|
|
||||||
TARGET_BRANCH="gh-pages"
|
|
||||||
|
|
||||||
function doCompile {
|
|
||||||
npm run typedoc
|
|
||||||
}
|
|
||||||
|
|
||||||
# Pull requests and commits to other branches shouldn't try to deploy, just build to verify
|
|
||||||
if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_BRANCH" != "$SOURCE_BRANCH" ]; then
|
|
||||||
echo "Skipping deploy; just doing a build."
|
|
||||||
doCompile
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Save some useful information
|
|
||||||
REPO=`git config remote.origin.url`
|
|
||||||
SSH_REPO=${REPO/https:\/\/github.com\//git@github.com:}
|
|
||||||
SHA=`git rev-parse --verify HEAD`
|
|
||||||
|
|
||||||
# Clone the existing gh-pages for this repo into docs/
|
|
||||||
# Create a new empty branch if gh-pages doesn't exist yet (should only happen on first deply)
|
|
||||||
git clone $REPO docs
|
|
||||||
cd docs
|
|
||||||
git checkout $TARGET_BRANCH || git checkout --orphan $TARGET_BRANCH
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
# Clean out existing contents
|
|
||||||
# echo "Cleaning out existing contents."
|
|
||||||
# rm -rf docs/*
|
|
||||||
|
|
||||||
# Run our compile script
|
|
||||||
doCompile
|
|
||||||
|
|
||||||
# Now let's go have some fun with the cloned repo
|
|
||||||
cd docs
|
|
||||||
git config user.name "Travis CI"
|
|
||||||
git config user.email "dolan_miu@hotmail.com"
|
|
||||||
ls
|
|
||||||
|
|
||||||
# add .nojekyll file
|
|
||||||
touch .nojekyll
|
|
||||||
|
|
||||||
# If there are no changes to the compiled out (e.g. this is a README update) then just bail.
|
|
||||||
if [ -z `git diff --exit-code` ]; then
|
|
||||||
echo "No changes to the output on this push; exiting."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Commit the "changes", i.e. the new version.
|
|
||||||
# The delta will show diffs between new and old versions.
|
|
||||||
git add .
|
|
||||||
git commit -m "Deploy to GitHub Pages: ${SHA}"
|
|
||||||
|
|
||||||
# Get the deploy key by using Travis's stored variables to decrypt deploy-key.enc
|
|
||||||
ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key"
|
|
||||||
ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv"
|
|
||||||
ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR}
|
|
||||||
ENCRYPTED_IV=${!ENCRYPTED_IV_VAR}
|
|
||||||
openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in deploy-key.enc -out deploy-key -d
|
|
||||||
chmod 600 deploy-key
|
|
||||||
eval `ssh-agent -s`
|
|
||||||
ssh-add deploy-key
|
|
||||||
|
|
||||||
# Now that we're all set up, we can push.
|
|
||||||
git push $SSH_REPO $TARGET_BRANCH
|
|
BIN
deploy-key.enc
BIN
deploy-key.enc
Binary file not shown.
12
package.json
12
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "docx",
|
"name": "docx",
|
||||||
"version": "3.2.0",
|
"version": "3.6.0",
|
||||||
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
|
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
|
||||||
"main": "build/index.js",
|
"main": "build/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -13,8 +13,9 @@
|
|||||||
"tsc": "rimraf ./build && tsc -p .",
|
"tsc": "rimraf ./build && tsc -p .",
|
||||||
"webpack": "rimraf ./build && webpack",
|
"webpack": "rimraf ./build && webpack",
|
||||||
"demo": "npm run build && node ./demo",
|
"demo": "npm run build && node ./demo",
|
||||||
"typedoc": "npm run build && typedoc --out docs/ src/ --module commonjs --target ES6 --disableOutputCheck",
|
"typedoc": "typedoc --out docs/ src/ --module commonjs --target ES6 --disableOutputCheck --excludePrivate --externalPattern \"**/*.spec.ts\"",
|
||||||
"style": "prettier -l --trailing-comma all --print-width 140 --arrow-parens always \"src/**/*.ts\"",
|
"style": "prettier -l \"src/**/*.ts\"",
|
||||||
|
"style.fix": "prettier \"src/**/*.ts\" --write",
|
||||||
"fix-types": "node types-absolute-fixer.js"
|
"fix-types": "node types-absolute-fixer.js"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
@ -45,6 +46,7 @@
|
|||||||
"@types/image-size": "0.0.29",
|
"@types/image-size": "0.0.29",
|
||||||
"@types/request-promise": "^4.1.41",
|
"@types/request-promise": "^4.1.41",
|
||||||
"archiver": "^2.1.1",
|
"archiver": "^2.1.1",
|
||||||
|
"fast-xml-parser": "^3.3.6",
|
||||||
"image-size": "^0.6.2",
|
"image-size": "^0.6.2",
|
||||||
"request": "^2.83.0",
|
"request": "^2.83.0",
|
||||||
"request-promise": "^4.2.2",
|
"request-promise": "^4.2.2",
|
||||||
@ -59,16 +61,18 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^3.4.35",
|
"@types/chai": "^3.4.35",
|
||||||
"@types/mocha": "^2.2.39",
|
"@types/mocha": "^2.2.39",
|
||||||
|
"@types/sinon": "^4.3.1",
|
||||||
"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",
|
||||||
"mocha": "^3.2.0",
|
"mocha": "^3.2.0",
|
||||||
"mocha-webpack": "^1.0.1",
|
"mocha-webpack": "^1.0.1",
|
||||||
"prettier": "^1.10.2",
|
"prettier": "^1.12.1",
|
||||||
"prompt": "^1.0.0",
|
"prompt": "^1.0.0",
|
||||||
"replace-in-file": "^3.1.0",
|
"replace-in-file": "^3.1.0",
|
||||||
"rimraf": "^2.5.2",
|
"rimraf": "^2.5.2",
|
||||||
"shelljs": "^0.7.7",
|
"shelljs": "^0.7.7",
|
||||||
|
"sinon": "^5.0.7",
|
||||||
"tslint": "^5.1.0",
|
"tslint": "^5.1.0",
|
||||||
"typedoc": "^0.9.0",
|
"typedoc": "^0.9.0",
|
||||||
"typescript": "2.6.2",
|
"typescript": "2.6.2",
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
export * from "./packer/local";
|
export * from "./packer/local";
|
||||||
export * from "./packer/express";
|
export * from "./packer/express";
|
||||||
export * from "./packer/packer";
|
export * from "./packer/packer";
|
||||||
|
export * from "./packer/stream";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as archiver from "archiver";
|
import * as archiver from "archiver";
|
||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import * as fs from "fs";
|
import { Writable } from "stream";
|
||||||
import * as xml from "xml";
|
import * as xml from "xml";
|
||||||
|
|
||||||
import { File } from "file";
|
import { File } from "file";
|
||||||
@ -19,7 +19,7 @@ export class Compiler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async compile(output: fs.WriteStream | express.Response): Promise<void> {
|
public async compile(output: Writable | express.Response): Promise<void> {
|
||||||
this.archive.pipe(output);
|
this.archive.pipe(output);
|
||||||
|
|
||||||
const xmlDocument = xml(this.formatter.format(this.file.Document), true);
|
const xmlDocument = xml(this.formatter.format(this.file.Document), true);
|
||||||
@ -34,6 +34,7 @@ export class Compiler {
|
|||||||
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 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 xmlFooter = xml(this.formatter.format(this.file.Footer.Footer));
|
||||||
const xmlHeaderRelationships = xml(this.formatter.format(this.file.Header.Relationships));
|
const xmlHeaderRelationships = xml(this.formatter.format(this.file.Header.Relationships));
|
||||||
const xmlFooterRelationships = xml(this.formatter.format(this.file.Footer.Relationships));
|
const xmlFooterRelationships = xml(this.formatter.format(this.file.Footer.Relationships));
|
||||||
@ -64,6 +65,10 @@ export class Compiler {
|
|||||||
name: "word/header1.xml",
|
name: "word/header1.xml",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.archive.append(xmlHeader2, {
|
||||||
|
name: "word/header2.xml",
|
||||||
|
});
|
||||||
|
|
||||||
this.archive.append(xmlFooter, {
|
this.archive.append(xmlFooter, {
|
||||||
name: "word/footer1.xml",
|
name: "word/footer1.xml",
|
||||||
});
|
});
|
||||||
|
43
src/export/packer/express.spec.ts
Normal file
43
src/export/packer/express.spec.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// tslint:disable:typedef space-before-function-paren
|
||||||
|
// tslint:disable:no-empty
|
||||||
|
// tslint:disable:no-any
|
||||||
|
import { assert } from "chai";
|
||||||
|
import { stub } from "sinon";
|
||||||
|
|
||||||
|
import { ExpressPacker } from "../../export/packer/express";
|
||||||
|
import { File, Paragraph } from "../../file";
|
||||||
|
|
||||||
|
describe("LocalPacker", () => {
|
||||||
|
let packer: ExpressPacker;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const file = new File({
|
||||||
|
creator: "Dolan Miu",
|
||||||
|
revision: "1",
|
||||||
|
lastModifiedBy: "Dolan Miu",
|
||||||
|
});
|
||||||
|
const paragraph = new Paragraph("test text");
|
||||||
|
const heading = new Paragraph("Hello world").heading1();
|
||||||
|
file.addParagraph(new Paragraph("title").title());
|
||||||
|
file.addParagraph(heading);
|
||||||
|
file.addParagraph(new Paragraph("heading 2").heading2());
|
||||||
|
file.addParagraph(paragraph);
|
||||||
|
|
||||||
|
const expressResMock = {
|
||||||
|
on: () => {},
|
||||||
|
attachment: () => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
packer = new ExpressPacker(file, expressResMock as any);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#pack()", () => {
|
||||||
|
it("should handle exception if it throws any", () => {
|
||||||
|
const compiler = stub((packer as any).packer, "compile");
|
||||||
|
compiler.throwsException();
|
||||||
|
return packer.pack("build/tests/test").catch((error) => {
|
||||||
|
assert.isDefined(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,5 +1,7 @@
|
|||||||
/* tslint:disable:typedef space-before-function-paren */
|
/* tslint:disable:typedef space-before-function-paren */
|
||||||
|
import { assert } from "chai";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
|
import { stub } from "sinon";
|
||||||
|
|
||||||
import { LocalPacker } from "../../export/packer/local";
|
import { LocalPacker } from "../../export/packer/local";
|
||||||
import { File, Paragraph } from "../../file";
|
import { File, Paragraph } from "../../file";
|
||||||
@ -29,14 +31,36 @@ describe("LocalPacker", () => {
|
|||||||
await packer.pack("build/tests/test");
|
await packer.pack("build/tests/test");
|
||||||
fs.statSync("build/tests/test.docx");
|
fs.statSync("build/tests/test.docx");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should handle exception if it throws any", () => {
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
const compiler = stub((packer as any).packer, "compile");
|
||||||
|
compiler.throwsException();
|
||||||
|
return packer.pack("build/tests/test").catch((error) => {
|
||||||
|
assert.isDefined(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#packPdf", () => {
|
describe("#packPdf", () => {
|
||||||
it("should create a standard PDF file", async function() {
|
it("should create a standard PDF file", async function() {
|
||||||
this.timeout(99999999);
|
this.timeout(99999999);
|
||||||
|
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
const pdfConverterConvert = stub((packer as any).pdfConverter, "convert");
|
||||||
|
pdfConverterConvert.returns("Test PDF Contents");
|
||||||
|
|
||||||
await packer.packPdf("build/tests/pdf-test");
|
await packer.packPdf("build/tests/pdf-test");
|
||||||
fs.statSync("build/tests/pdf-test.pdf");
|
fs.statSync("build/tests/pdf-test.pdf");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should handle exception if it throws any", () => {
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
const compiler = stub((packer as any).packer, "compile");
|
||||||
|
compiler.throwsException();
|
||||||
|
return packer.packPdf("build/tests/pdf-test").catch((error) => {
|
||||||
|
assert.isDefined(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
25
src/export/packer/stream.ts
Normal file
25
src/export/packer/stream.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Readable, Transform } from "stream";
|
||||||
|
import { File } from "../../file";
|
||||||
|
import { Compiler } from "./compiler";
|
||||||
|
import { IPacker } from "./packer";
|
||||||
|
|
||||||
|
class Pipe extends Transform {
|
||||||
|
public _transform(chunk: Buffer | string, encoding: string, callback: () => void): void {
|
||||||
|
this.push(chunk, encoding);
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StreamPacker implements IPacker {
|
||||||
|
private readonly compiler: Compiler;
|
||||||
|
|
||||||
|
constructor(file: File) {
|
||||||
|
this.compiler = new Compiler(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public pack(): Readable {
|
||||||
|
const pipe = new Pipe();
|
||||||
|
this.compiler.compile(pipe);
|
||||||
|
return pipe;
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,9 @@ export class ContentTypes extends XmlComponent {
|
|||||||
this.root.push(
|
this.root.push(
|
||||||
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/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.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"));
|
||||||
|
@ -10,6 +10,7 @@ export interface IPropertiesOptions {
|
|||||||
description?: string;
|
description?: string;
|
||||||
lastModifiedBy?: string;
|
lastModifiedBy?: string;
|
||||||
revision?: string;
|
revision?: string;
|
||||||
|
externalStyles?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CoreProperties extends XmlComponent {
|
export class CoreProperties extends XmlComponent {
|
||||||
|
@ -2,12 +2,12 @@ import { XmlComponent } from "file/xml-components";
|
|||||||
import { HeaderReferenceAttributes } from "./header-reference-attributes";
|
import { HeaderReferenceAttributes } from "./header-reference-attributes";
|
||||||
|
|
||||||
export class HeaderReference extends XmlComponent {
|
export class HeaderReference extends XmlComponent {
|
||||||
constructor() {
|
constructor(order: string, refID: number) {
|
||||||
super("w:headerReference");
|
super("w:headerReference");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new HeaderReferenceAttributes({
|
new HeaderReferenceAttributes({
|
||||||
type: "default",
|
type: order,
|
||||||
id: `rId${3}`,
|
id: `rId${refID}`,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ 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 } from "./page-size/page-size-attributes";
|
||||||
|
import { TitlePage } from "./title-page/title-page";
|
||||||
|
|
||||||
export type SectionPropertiesOptions = IPageSizeAttributes & IPageMarginAttributes & IColumnsAttributes & IDocGridAttributesProperties;
|
export type SectionPropertiesOptions = IPageSizeAttributes & IPageMarginAttributes & IColumnsAttributes & IDocGridAttributesProperties;
|
||||||
|
|
||||||
@ -30,6 +31,7 @@ export class SectionProperties extends XmlComponent {
|
|||||||
space: 708,
|
space: 708,
|
||||||
linePitch: 360,
|
linePitch: 360,
|
||||||
orientation: "portrait",
|
orientation: "portrait",
|
||||||
|
differentFirstPageHeader: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mergedOptions = {
|
const mergedOptions = {
|
||||||
@ -51,7 +53,13 @@ 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());
|
this.root.push(new HeaderReference("default", 3));
|
||||||
|
|
||||||
|
if (mergedOptions.differentFirstPageHeader) {
|
||||||
|
this.root.push(new HeaderReference("first", 5));
|
||||||
|
this.root.push(new TitlePage());
|
||||||
|
}
|
||||||
|
|
||||||
this.root.push(new FooterReference());
|
this.root.push(new FooterReference());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export interface IHeaderReferenceAttributes {
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TitlePageAttributes extends XmlAttributeComponent<IHeaderReferenceAttributes> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
value: "w:val",
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "../../../../../export/formatter";
|
||||||
|
import { TitlePage } from "./title-page";
|
||||||
|
|
||||||
|
describe("PageSize", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create title page property for different first page header", () => {
|
||||||
|
const properties = new TitlePage();
|
||||||
|
const tree = new Formatter().format(properties);
|
||||||
|
|
||||||
|
expect(Object.keys(tree)).to.deep.equal(["w:titlePg"]);
|
||||||
|
expect(tree["w:titlePg"]).to.be.an.instanceof(Array);
|
||||||
|
expect(tree["w:titlePg"][0]).to.deep.equal({ _attr: { "w:val": "1" } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,13 @@
|
|||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { TitlePageAttributes } from "./title-page-attributes";
|
||||||
|
|
||||||
|
export class TitlePage extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("w:titlePg");
|
||||||
|
this.root.push(
|
||||||
|
new TitlePageAttributes({
|
||||||
|
value: "1",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -57,17 +57,17 @@ export class Document extends XmlComponent {
|
|||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
public addDrawing(imageData: IMediaData): void {
|
public addDrawing(pictureParagraph: Paragraph): void {
|
||||||
|
this.body.push(pictureParagraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
public createDrawing(imageData: IMediaData): PictureRun {
|
||||||
const paragraph = new Paragraph();
|
const paragraph = new Paragraph();
|
||||||
const run = new PictureRun(imageData);
|
const run = new PictureRun(imageData);
|
||||||
|
|
||||||
paragraph.addRun(run);
|
paragraph.addRun(run);
|
||||||
|
this.addDrawing(paragraph);
|
||||||
|
|
||||||
this.body.push(paragraph);
|
return run;
|
||||||
}
|
|
||||||
|
|
||||||
public createDrawing(imageData: IMediaData): void {
|
|
||||||
this.addDrawing(imageData);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ import { XmlComponent } from "file/xml-components";
|
|||||||
import { Inline } from "./inline";
|
import { Inline } from "./inline";
|
||||||
|
|
||||||
export class Drawing extends XmlComponent {
|
export class Drawing extends XmlComponent {
|
||||||
|
private inline: Inline;
|
||||||
|
|
||||||
constructor(imageData: IMediaData) {
|
constructor(imageData: IMediaData) {
|
||||||
super("w:drawing");
|
super("w:drawing");
|
||||||
|
|
||||||
@ -10,6 +12,12 @@ export class Drawing extends XmlComponent {
|
|||||||
throw new Error("imageData cannot be undefined");
|
throw new Error("imageData cannot be undefined");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.root.push(new Inline(imageData.referenceId, imageData.dimensions));
|
this.inline = new Inline(imageData.referenceId, imageData.dimensions);
|
||||||
|
|
||||||
|
this.root.push(this.inline);
|
||||||
|
}
|
||||||
|
|
||||||
|
public scale(factorX: number, factorY: number): void {
|
||||||
|
this.inline.scale(factorX, factorY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,23 @@ import { XmlComponent } from "file/xml-components";
|
|||||||
import { ExtentAttributes } from "./extent-attributes";
|
import { ExtentAttributes } from "./extent-attributes";
|
||||||
|
|
||||||
export class Extent extends XmlComponent {
|
export class Extent extends XmlComponent {
|
||||||
|
private attributes: ExtentAttributes;
|
||||||
|
|
||||||
constructor(x: number, y: number) {
|
constructor(x: number, y: number) {
|
||||||
super("wp:extent");
|
super("wp:extent");
|
||||||
|
|
||||||
this.root.push(
|
this.attributes = new ExtentAttributes({
|
||||||
new ExtentAttributes({
|
cx: x,
|
||||||
cx: x,
|
cy: y,
|
||||||
cy: y,
|
});
|
||||||
}),
|
|
||||||
);
|
this.root.push(this.attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setXY(x: number, y: number): void {
|
||||||
|
this.attributes.set({
|
||||||
|
cx: x,
|
||||||
|
cy: y,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ import { GraphicDataAttributes } from "./graphic-data-attribute";
|
|||||||
import { Pic } from "./pic";
|
import { Pic } from "./pic";
|
||||||
|
|
||||||
export class GraphicData extends XmlComponent {
|
export class GraphicData extends XmlComponent {
|
||||||
|
private pic: Pic;
|
||||||
|
|
||||||
constructor(referenceId: number, x: number, y: number) {
|
constructor(referenceId: number, x: number, y: number) {
|
||||||
super("a:graphicData");
|
super("a:graphicData");
|
||||||
|
|
||||||
@ -12,6 +14,12 @@ export class GraphicData extends XmlComponent {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.root.push(new Pic(referenceId, x, y));
|
this.pic = new Pic(referenceId, x, y);
|
||||||
|
|
||||||
|
this.root.push(this.pic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setXY(x: number, y: number): void {
|
||||||
|
this.pic.setXY(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import { PicAttributes } from "./pic-attributes";
|
|||||||
import { ShapeProperties } from "./shape-properties/shape-properties";
|
import { ShapeProperties } from "./shape-properties/shape-properties";
|
||||||
|
|
||||||
export class Pic extends XmlComponent {
|
export class Pic extends XmlComponent {
|
||||||
|
private shapeProperties: ShapeProperties;
|
||||||
|
|
||||||
constructor(referenceId: number, x: number, y: number) {
|
constructor(referenceId: number, x: number, y: number) {
|
||||||
super("pic:pic");
|
super("pic:pic");
|
||||||
|
|
||||||
@ -14,8 +16,15 @@ export class Pic extends XmlComponent {
|
|||||||
xmlns: "http://schemas.openxmlformats.org/drawingml/2006/picture",
|
xmlns: "http://schemas.openxmlformats.org/drawingml/2006/picture",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.shapeProperties = new ShapeProperties(x, y);
|
||||||
|
|
||||||
this.root.push(new NonVisualPicProperties());
|
this.root.push(new NonVisualPicProperties());
|
||||||
this.root.push(new BlipFill(referenceId));
|
this.root.push(new BlipFill(referenceId));
|
||||||
this.root.push(new ShapeProperties(x, y));
|
this.root.push(new ShapeProperties(x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setXY(x: number, y: number): void {
|
||||||
|
this.shapeProperties.setXY(x, y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,23 @@ import { XmlComponent } from "file/xml-components";
|
|||||||
import { ExtentsAttributes } from "./extents-attributes";
|
import { ExtentsAttributes } from "./extents-attributes";
|
||||||
|
|
||||||
export class Extents extends XmlComponent {
|
export class Extents extends XmlComponent {
|
||||||
|
private attributes: ExtentsAttributes;
|
||||||
|
|
||||||
constructor(x: number, y: number) {
|
constructor(x: number, y: number) {
|
||||||
super("a:ext");
|
super("a:ext");
|
||||||
|
|
||||||
this.root.push(
|
this.attributes = new ExtentsAttributes({
|
||||||
new ExtentsAttributes({
|
cx: x,
|
||||||
cx: x,
|
cy: y,
|
||||||
cy: y,
|
});
|
||||||
}),
|
|
||||||
);
|
this.root.push(this.attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setXY(x: number, y: number): void {
|
||||||
|
this.attributes.set({
|
||||||
|
cx: x,
|
||||||
|
cy: y,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,18 @@ import { Extents } from "./extents/extents";
|
|||||||
import { Offset } from "./offset/off";
|
import { Offset } from "./offset/off";
|
||||||
|
|
||||||
export class Form extends XmlComponent {
|
export class Form extends XmlComponent {
|
||||||
|
private extents: Extents;
|
||||||
|
|
||||||
constructor(x: number, y: number) {
|
constructor(x: number, y: number) {
|
||||||
super("a:xfrm");
|
super("a:xfrm");
|
||||||
|
|
||||||
this.root.push(new Extents(x, y));
|
this.extents = new Extents(x, y);
|
||||||
|
|
||||||
|
this.root.push(this.extents);
|
||||||
this.root.push(new Offset());
|
this.root.push(new Offset());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setXY(x: number, y: number): void {
|
||||||
|
this.extents.setXY(x, y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ import { PresetGeometry } from "./preset-geometry/preset-geometry";
|
|||||||
import { ShapePropertiesAttributes } from "./shape-properties-attributes";
|
import { ShapePropertiesAttributes } from "./shape-properties-attributes";
|
||||||
|
|
||||||
export class ShapeProperties extends XmlComponent {
|
export class ShapeProperties extends XmlComponent {
|
||||||
|
private form: Form;
|
||||||
|
|
||||||
constructor(x: number, y: number) {
|
constructor(x: number, y: number) {
|
||||||
super("pic:spPr");
|
super("pic:spPr");
|
||||||
|
|
||||||
@ -16,9 +18,15 @@ export class ShapeProperties extends XmlComponent {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.root.push(new Form(x, y));
|
this.form = new Form(x, y);
|
||||||
|
|
||||||
|
this.root.push(this.form);
|
||||||
this.root.push(new PresetGeometry());
|
this.root.push(new PresetGeometry());
|
||||||
// this.root.push(new NoFill());
|
// this.root.push(new NoFill());
|
||||||
// this.root.push(new Outline());
|
// this.root.push(new Outline());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setXY(x: number, y: number): void {
|
||||||
|
this.form.setXY(x, y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
33
src/file/drawing/inline/graphic/graphic.ts
Normal file
33
src/file/drawing/inline/graphic/graphic.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
import { GraphicData } from "./graphic-data";
|
||||||
|
|
||||||
|
interface IGraphicProperties {
|
||||||
|
a: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GraphicAttributes extends XmlAttributeComponent<IGraphicProperties> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
a: "xmlns:a",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Graphic extends XmlComponent {
|
||||||
|
private data: GraphicData;
|
||||||
|
|
||||||
|
constructor(referenceId: number, x: number, y: number) {
|
||||||
|
super("a:graphic");
|
||||||
|
this.root.push(
|
||||||
|
new GraphicAttributes({
|
||||||
|
a: "http://schemas.openxmlformats.org/drawingml/2006/main",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.data = new GraphicData(referenceId, x, y);
|
||||||
|
|
||||||
|
this.root.push(this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setXY(x: number, y: number): void {
|
||||||
|
this.data.setXY(x, y);
|
||||||
|
}
|
||||||
|
}
|
@ -1,24 +1 @@
|
|||||||
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
export * from "./graphic";
|
||||||
import { GraphicData } from "./graphic-data";
|
|
||||||
|
|
||||||
interface IGraphicProperties {
|
|
||||||
a: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
class GraphicAttributes extends XmlAttributeComponent<IGraphicProperties> {
|
|
||||||
protected xmlKeys = {
|
|
||||||
a: "xmlns:a",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Graphic extends XmlComponent {
|
|
||||||
constructor(referenceId: number, x: number, y: number) {
|
|
||||||
super("a:graphic");
|
|
||||||
this.root.push(
|
|
||||||
new GraphicAttributes({
|
|
||||||
a: "http://schemas.openxmlformats.org/drawingml/2006/main",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
this.root.push(new GraphicData(referenceId, x, y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -9,7 +9,10 @@ import { GraphicFrameProperties } from "./graphic-frame/graphic-frame-properties
|
|||||||
import { InlineAttributes } from "./inline-attributes";
|
import { InlineAttributes } from "./inline-attributes";
|
||||||
|
|
||||||
export class Inline extends XmlComponent {
|
export class Inline extends XmlComponent {
|
||||||
constructor(referenceId: number, dimensions: IMediaDataDimensions) {
|
private extent: Extent;
|
||||||
|
private graphic: Graphic;
|
||||||
|
|
||||||
|
constructor(referenceId: number, private dimensions: IMediaDataDimensions) {
|
||||||
super("wp:inline");
|
super("wp:inline");
|
||||||
|
|
||||||
this.root.push(
|
this.root.push(
|
||||||
@ -21,10 +24,21 @@ export class Inline extends XmlComponent {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.root.push(new Extent(dimensions.emus.x, dimensions.emus.y));
|
this.extent = new Extent(dimensions.emus.x, dimensions.emus.y);
|
||||||
|
this.graphic = new Graphic(referenceId, dimensions.emus.x, dimensions.emus.y);
|
||||||
|
|
||||||
|
this.root.push(this.extent);
|
||||||
this.root.push(new EffectExtent());
|
this.root.push(new EffectExtent());
|
||||||
this.root.push(new DocProperties());
|
this.root.push(new DocProperties());
|
||||||
this.root.push(new GraphicFrameProperties());
|
this.root.push(new GraphicFrameProperties());
|
||||||
this.root.push(new Graphic(referenceId, dimensions.emus.x, dimensions.emus.y));
|
this.root.push(this.graphic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public scale(factorX: number, factorY: number): void {
|
||||||
|
const newX = Math.round(this.dimensions.emus.x * factorX);
|
||||||
|
const newY = Math.round(this.dimensions.emus.y * factorY);
|
||||||
|
|
||||||
|
this.extent.setXY(newX, newY);
|
||||||
|
this.graphic.setXY(newX, newY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
|
import { IMediaData } from "file/media";
|
||||||
import { AppProperties } from "./app-properties/app-properties";
|
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 { 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 { HeaderWrapper } from "./header-wrapper";
|
import { FirstPageHeaderWrapper, HeaderWrapper } from "./header-wrapper";
|
||||||
import { Media } from "./media";
|
import { Media } from "./media";
|
||||||
import { Numbering } from "./numbering";
|
import { Numbering } from "./numbering";
|
||||||
import { Paragraph } from "./paragraph";
|
import { Hyperlink, Paragraph, PictureRun } from "./paragraph";
|
||||||
import { Relationships } from "./relationships";
|
import { Relationships } from "./relationships";
|
||||||
import { Styles } from "./styles";
|
import { Styles } from "./styles";
|
||||||
|
import { ExternalStylesFactory } from "./styles/external-styles-factory";
|
||||||
import { DefaultStylesFactory } from "./styles/factory";
|
import { DefaultStylesFactory } from "./styles/factory";
|
||||||
import { Table } from "./table";
|
import { Table } from "./table";
|
||||||
|
|
||||||
@ -22,14 +24,15 @@ export class File {
|
|||||||
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 firstPageHeaderWrapper: FirstPageHeaderWrapper;
|
||||||
|
|
||||||
private readonly footerWrapper: FooterWrapper;
|
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) {
|
constructor(options?: IPropertiesOptions, sectionPropertiesOptions?: SectionPropertiesOptions) {
|
||||||
this.document = new Document(sectionPropertiesOptions);
|
this.document = new Document(sectionPropertiesOptions);
|
||||||
const stylesFactory = new DefaultStylesFactory();
|
|
||||||
this.styles = stylesFactory.newInstance();
|
|
||||||
|
|
||||||
if (!options) {
|
if (!options) {
|
||||||
options = {
|
options = {
|
||||||
@ -39,6 +42,14 @@ export class File {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.externalStyles) {
|
||||||
|
const stylesFactory = new ExternalStylesFactory();
|
||||||
|
this.styles = stylesFactory.newInstance(options.externalStyles);
|
||||||
|
} else {
|
||||||
|
const stylesFactory = new DefaultStylesFactory();
|
||||||
|
this.styles = stylesFactory.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
||||||
@ -57,13 +68,23 @@ export class File {
|
|||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
|
||||||
"header1.xml",
|
"header1.xml",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.docRelationships.createRelationship(
|
||||||
|
5,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header",
|
||||||
|
"header2.xml",
|
||||||
|
);
|
||||||
|
|
||||||
this.docRelationships.createRelationship(
|
this.docRelationships.createRelationship(
|
||||||
4,
|
4,
|
||||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer",
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer",
|
||||||
"footer1.xml",
|
"footer1.xml",
|
||||||
);
|
);
|
||||||
this.media = new Media();
|
this.media = new Media();
|
||||||
|
|
||||||
this.headerWrapper = new HeaderWrapper(this.media);
|
this.headerWrapper = new HeaderWrapper(this.media);
|
||||||
|
this.firstPageHeaderWrapper = new FirstPageHeaderWrapper(this.media);
|
||||||
|
|
||||||
this.footerWrapper = new FooterWrapper(this.media);
|
this.footerWrapper = new FooterWrapper(this.media);
|
||||||
this.contentTypes = new ContentTypes();
|
this.contentTypes = new ContentTypes();
|
||||||
this.fileRelationships = new Relationships();
|
this.fileRelationships = new Relationships();
|
||||||
@ -101,14 +122,36 @@ export class File {
|
|||||||
return this.document.createTable(rows, cols);
|
return this.document.createTable(rows, cols);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createImage(image: string): void {
|
public createImage(image: string): PictureRun {
|
||||||
const mediaData = this.media.addMedia(image, this.docRelationships.RelationshipCount);
|
const mediaData = this.media.addMedia(image, this.docRelationships.RelationshipCount);
|
||||||
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",
|
||||||
`media/${mediaData.fileName}`,
|
`media/${mediaData.fileName}`,
|
||||||
);
|
);
|
||||||
this.document.createDrawing(mediaData);
|
return this.document.createDrawing(mediaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public createImageData(imageName: string, data: Buffer, width?: number, height?: number): IMediaData {
|
||||||
|
const mediaData = this.media.addMediaWithData(imageName, data, this.docRelationships.RelationshipCount, width, height);
|
||||||
|
this.docRelationships.createRelationship(
|
||||||
|
mediaData.referenceId,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||||
|
`media/${mediaData.fileName}`,
|
||||||
|
);
|
||||||
|
return mediaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public createHyperlink(link: string, text?: string): Hyperlink {
|
||||||
|
text = text === undefined ? link : text;
|
||||||
|
const hyperlink = new Hyperlink(text, this.docRelationships.RelationshipCount);
|
||||||
|
this.docRelationships.createRelationship(
|
||||||
|
hyperlink.linkId,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
||||||
|
link,
|
||||||
|
"External",
|
||||||
|
);
|
||||||
|
return hyperlink;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get Document(): Document {
|
public get Document(): Document {
|
||||||
@ -143,6 +186,10 @@ export class File {
|
|||||||
return this.headerWrapper;
|
return this.headerWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get firstPageHeader(): FirstPageHeaderWrapper {
|
||||||
|
return this.firstPageHeaderWrapper;
|
||||||
|
}
|
||||||
|
|
||||||
public get Footer(): FooterWrapper {
|
public get Footer(): FooterWrapper {
|
||||||
return this.footerWrapper;
|
return this.footerWrapper;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,56 @@ 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;
|
||||||
|
@ -5,3 +5,4 @@ export * from "./numbering";
|
|||||||
export * from "./media";
|
export * from "./media";
|
||||||
export * from "./drawing";
|
export * from "./drawing";
|
||||||
export * from "./styles";
|
export * from "./styles";
|
||||||
|
export * from "./xml-components";
|
||||||
|
@ -13,8 +13,8 @@ export interface IMediaDataDimensions {
|
|||||||
|
|
||||||
export interface IMediaData {
|
export interface IMediaData {
|
||||||
referenceId: number;
|
referenceId: number;
|
||||||
stream: fs.ReadStream;
|
stream: fs.ReadStream | Buffer;
|
||||||
path: string;
|
path?: string;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
dimensions: IMediaDataDimensions;
|
dimensions: IMediaDataDimensions;
|
||||||
}
|
}
|
||||||
|
@ -24,10 +24,34 @@ export class Media {
|
|||||||
public addMedia(filePath: string, relationshipsCount: number): IMediaData {
|
public addMedia(filePath: string, relationshipsCount: 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addMediaWithData(fileName: string, data: Buffer, relationshipsCount: number, width?: number, height?: number): IMediaData {
|
||||||
|
const key = fileName;
|
||||||
|
let dimensions;
|
||||||
|
if (width && height) {
|
||||||
|
dimensions = {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
dimensions = sizeOf(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.createMedia(key, relationshipsCount, dimensions, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createMedia(
|
||||||
|
key: string,
|
||||||
|
relationshipsCount: number,
|
||||||
|
dimensions: { width: number; height: number },
|
||||||
|
data: fs.ReadStream | Buffer,
|
||||||
|
filePath?: string,
|
||||||
|
): IMediaData {
|
||||||
const imageData = {
|
const imageData = {
|
||||||
referenceId: this.map.size + relationshipsCount + 1,
|
referenceId: this.map.size + relationshipsCount + 1,
|
||||||
stream: fs.createReadStream(filePath),
|
stream: data,
|
||||||
path: filePath,
|
path: filePath,
|
||||||
fileName: key,
|
fileName: key,
|
||||||
dimensions: {
|
dimensions: {
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from "./numbering";
|
export * from "./numbering";
|
||||||
|
export * from "./abstract-numbering";
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { XmlComponent } from "file/xml-components";
|
import { Indent } from "file/paragraph";
|
||||||
|
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
||||||
import { DocumentAttributes } from "../document/document-attributes";
|
import { DocumentAttributes } from "../document/document-attributes";
|
||||||
import { Indent } from "../paragraph/formatting";
|
|
||||||
import { RunFonts } from "../paragraph/run/run-fonts";
|
|
||||||
import { AbstractNumbering } from "./abstract-numbering";
|
import { AbstractNumbering } from "./abstract-numbering";
|
||||||
import { Num } from "./num";
|
import { Num } from "./num";
|
||||||
|
|
||||||
export class Numbering extends XmlComponent {
|
export class Numbering extends XmlComponent {
|
||||||
private nextId: number;
|
private nextId: number;
|
||||||
|
|
||||||
|
private abstractNumbering: XmlComponent[] = [];
|
||||||
|
private concreteNumbering: XmlComponent[] = [];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("w:numbering");
|
super("w:numbering");
|
||||||
this.root.push(
|
this.root.push(
|
||||||
@ -36,63 +38,42 @@ export class Numbering extends XmlComponent {
|
|||||||
|
|
||||||
const abstractNumbering = this.createAbstractNumbering();
|
const abstractNumbering = this.createAbstractNumbering();
|
||||||
|
|
||||||
abstractNumbering
|
abstractNumbering.createLevel(0, "bullet", "\u25CF", "left").addParagraphProperty(new Indent({ left: 720, hanging: 360 }));
|
||||||
.createLevel(0, "bullet", "•", "left")
|
|
||||||
.addParagraphProperty(new Indent({ left: 720, hanging: 360 }))
|
|
||||||
.addRunProperty(new RunFonts("Symbol", "default"));
|
|
||||||
|
|
||||||
abstractNumbering
|
abstractNumbering.createLevel(1, "bullet", "\u25CB", "left").addParagraphProperty(new Indent({ left: 1440, hanging: 360 }));
|
||||||
.createLevel(1, "bullet", "o", "left")
|
|
||||||
.addParagraphProperty(new Indent({ left: 1440, hanging: 360 }))
|
|
||||||
.addRunProperty(new RunFonts("Courier New", "default"));
|
|
||||||
|
|
||||||
abstractNumbering
|
abstractNumbering.createLevel(2, "bullet", "\u25A0", "left").addParagraphProperty(new Indent({ left: 2160, hanging: 360 }));
|
||||||
.createLevel(2, "bullet", "•", "left")
|
|
||||||
.addParagraphProperty(new Indent({ left: 2160, hanging: 360 }))
|
|
||||||
.addRunProperty(new RunFonts("Wingdings", "default"));
|
|
||||||
|
|
||||||
abstractNumbering
|
abstractNumbering.createLevel(3, "bullet", "\u25CF", "left").addParagraphProperty(new Indent({ left: 2880, hanging: 360 }));
|
||||||
.createLevel(3, "bullet", "•", "left")
|
|
||||||
.addParagraphProperty(new Indent({ left: 2880, hanging: 360 }))
|
|
||||||
.addRunProperty(new RunFonts("Symbol", "default"));
|
|
||||||
|
|
||||||
abstractNumbering
|
abstractNumbering.createLevel(4, "bullet", "\u25CB", "left").addParagraphProperty(new Indent({ left: 3600, hanging: 360 }));
|
||||||
.createLevel(4, "bullet", "o", "left")
|
|
||||||
.addParagraphProperty(new Indent({ left: 3600, hanging: 360 }))
|
|
||||||
.addRunProperty(new RunFonts("Courier New", "default"));
|
|
||||||
|
|
||||||
abstractNumbering
|
abstractNumbering.createLevel(5, "bullet", "\u25A0", "left").addParagraphProperty(new Indent({ left: 4320, hanging: 360 }));
|
||||||
.createLevel(5, "bullet", "•", "left")
|
|
||||||
.addParagraphProperty(new Indent({ left: 4320, hanging: 360 }))
|
|
||||||
.addRunProperty(new RunFonts("Wingdings", "default"));
|
|
||||||
|
|
||||||
abstractNumbering
|
abstractNumbering.createLevel(6, "bullet", "\u25CF", "left").addParagraphProperty(new Indent({ left: 5040, hanging: 360 }));
|
||||||
.createLevel(6, "bullet", "•", "left")
|
|
||||||
.addParagraphProperty(new Indent({ left: 5040, hanging: 360 }))
|
|
||||||
.addRunProperty(new RunFonts("Symbol", "default"));
|
|
||||||
|
|
||||||
abstractNumbering
|
abstractNumbering.createLevel(7, "bullet", "\u25CB", "left").addParagraphProperty(new Indent({ left: 5760, hanging: 360 }));
|
||||||
.createLevel(7, "bullet", "o", "left")
|
|
||||||
.addParagraphProperty(new Indent({ left: 5760, hanging: 360 }))
|
|
||||||
.addRunProperty(new RunFonts("Courier New", "default"));
|
|
||||||
|
|
||||||
abstractNumbering
|
abstractNumbering.createLevel(8, "bullet", "\u25A0", "left").addParagraphProperty(new Indent({ left: 6480, hanging: 360 }));
|
||||||
.createLevel(8, "bullet", "•", "left")
|
|
||||||
.addParagraphProperty(new Indent({ left: 6480, hanging: 360 }))
|
|
||||||
.addRunProperty(new RunFonts("Wingdings", "default"));
|
|
||||||
|
|
||||||
this.createConcreteNumbering(abstractNumbering);
|
this.createConcreteNumbering(abstractNumbering);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createAbstractNumbering(): AbstractNumbering {
|
public createAbstractNumbering(): AbstractNumbering {
|
||||||
const num = new AbstractNumbering(this.nextId++);
|
const num = new AbstractNumbering(this.nextId++);
|
||||||
this.root.push(num);
|
this.abstractNumbering.push(num);
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
public createConcreteNumbering(abstractNumbering: AbstractNumbering): Num {
|
public createConcreteNumbering(abstractNumbering: AbstractNumbering): Num {
|
||||||
const num = new Num(this.nextId++, abstractNumbering.id);
|
const num = new Num(this.nextId++, abstractNumbering.id);
|
||||||
this.root.push(num);
|
this.concreteNumbering.push(num);
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public prepForXml(): IXmlableObject {
|
||||||
|
this.abstractNumbering.forEach((x) => this.root.push(x));
|
||||||
|
this.concreteNumbering.forEach((x) => this.root.push(x));
|
||||||
|
return super.prepForXml();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,3 +2,4 @@ export * from "./formatting";
|
|||||||
export * from "./paragraph";
|
export * from "./paragraph";
|
||||||
export * from "./properties";
|
export * from "./properties";
|
||||||
export * from "./run";
|
export * from "./run";
|
||||||
|
export * from "./links";
|
||||||
|
13
src/file/paragraph/links/hyperlink-attributes.ts
Normal file
13
src/file/paragraph/links/hyperlink-attributes.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export interface IHyperlinkAttributesProperties {
|
||||||
|
id?: string;
|
||||||
|
history: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HyperlinkAttributes extends XmlAttributeComponent<IHyperlinkAttributesProperties> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
id: "r:id",
|
||||||
|
history: "w:history",
|
||||||
|
};
|
||||||
|
}
|
40
src/file/paragraph/links/hyperlink.spec.ts
Normal file
40
src/file/paragraph/links/hyperlink.spec.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { assert, expect } from "chai";
|
||||||
|
|
||||||
|
import { Formatter } from "../../../export/formatter";
|
||||||
|
import { Utility } from "../../../tests/utility";
|
||||||
|
import { Hyperlink } from "./";
|
||||||
|
|
||||||
|
describe("Hyperlink", () => {
|
||||||
|
let hyperlink: Hyperlink;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
hyperlink = new Hyperlink("https://example.com", 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a hyperlink with correct root key", () => {
|
||||||
|
const newJson = Utility.jsonify(hyperlink);
|
||||||
|
assert.equal(newJson.rootKey, "w:hyperlink");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a hyperlink with right attributes", () => {
|
||||||
|
const newJson = Utility.jsonify(hyperlink);
|
||||||
|
const attributes = {
|
||||||
|
id: "rId1",
|
||||||
|
history: 1,
|
||||||
|
};
|
||||||
|
assert.equal(JSON.stringify(newJson.root[0].root), JSON.stringify(attributes));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should create a hyperlink with a run component", () => {
|
||||||
|
const tree = new Formatter().format(hyperlink);
|
||||||
|
const runJson = {
|
||||||
|
"w:r": [
|
||||||
|
{ "w:rPr": [{ "w:rStyle": [{ _attr: { "w:val": "Hyperlink" } }] }] },
|
||||||
|
{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "https://example.com"] },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
expect(tree["w:hyperlink"][1]).to.deep.equal(runJson);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
21
src/file/paragraph/links/hyperlink.ts
Normal file
21
src/file/paragraph/links/hyperlink.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// http://officeopenxml.com/WPhyperlink.php
|
||||||
|
|
||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { TextRun } from "../run";
|
||||||
|
import { HyperlinkAttributes } from "./hyperlink-attributes";
|
||||||
|
|
||||||
|
export class Hyperlink extends XmlComponent {
|
||||||
|
public linkId: number;
|
||||||
|
|
||||||
|
constructor(text: string, relationshipsCount: number) {
|
||||||
|
super("w:hyperlink");
|
||||||
|
|
||||||
|
this.linkId = relationshipsCount + 1;
|
||||||
|
const attributes = new HyperlinkAttributes({
|
||||||
|
id: `rId${this.linkId}`,
|
||||||
|
history: 1,
|
||||||
|
});
|
||||||
|
this.root.push(attributes);
|
||||||
|
this.root.push(new TextRun(text).style("Hyperlink"));
|
||||||
|
}
|
||||||
|
}
|
1
src/file/paragraph/links/index.ts
Normal file
1
src/file/paragraph/links/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./hyperlink";
|
@ -162,7 +162,7 @@ describe("Paragraph", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("#bullet()", () => {
|
describe("#bullet()", () => {
|
||||||
it("should add list paragraph style to JSON", () => {
|
it("should default to 0 indent level if no bullet was specified", () => {
|
||||||
paragraph.bullet();
|
paragraph.bullet();
|
||||||
const tree = new Formatter().format(paragraph);
|
const tree = new Formatter().format(paragraph);
|
||||||
expect(tree)
|
expect(tree)
|
||||||
@ -178,8 +178,24 @@ describe("Paragraph", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should add list paragraph style to JSON", () => {
|
||||||
|
paragraph.bullet(0);
|
||||||
|
const tree = new Formatter().format(paragraph);
|
||||||
|
expect(tree)
|
||||||
|
.to.have.property("w:p")
|
||||||
|
.which.is.an("array")
|
||||||
|
.which.has.length.at.least(1);
|
||||||
|
expect(tree["w:p"][0])
|
||||||
|
.to.have.property("w:pPr")
|
||||||
|
.which.is.an("array")
|
||||||
|
.which.has.length.at.least(1);
|
||||||
|
expect(tree["w:p"][0]["w:pPr"][0]).to.deep.equal({
|
||||||
|
"w:pStyle": [{ _attr: { "w:val": "ListParagraph" } }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("it should add numbered properties", () => {
|
it("it should add numbered properties", () => {
|
||||||
paragraph.bullet();
|
paragraph.bullet(1);
|
||||||
const tree = new Formatter().format(paragraph);
|
const tree = new Formatter().format(paragraph);
|
||||||
expect(tree)
|
expect(tree)
|
||||||
.to.have.property("w:p")
|
.to.have.property("w:p")
|
||||||
@ -190,7 +206,7 @@ describe("Paragraph", () => {
|
|||||||
.which.is.an("array")
|
.which.is.an("array")
|
||||||
.which.has.length.at.least(2);
|
.which.has.length.at.least(2);
|
||||||
expect(tree["w:p"][0]["w:pPr"][1]).to.deep.equal({
|
expect(tree["w:p"][0]["w:pPr"][1]).to.deep.equal({
|
||||||
"w:numPr": [{ "w:ilvl": [{ _attr: { "w:val": 0 } }] }, { "w:numId": [{ _attr: { "w:val": 1 } }] }],
|
"w:numPr": [{ "w:ilvl": [{ _attr: { "w:val": 1 } }] }, { "w:numId": [{ _attr: { "w:val": 1 } }] }],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -13,6 +13,7 @@ import { ISpacingProperties, Spacing } from "./formatting/spacing";
|
|||||||
import { Style } from "./formatting/style";
|
import { Style } from "./formatting/style";
|
||||||
import { CenterTabStop, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop";
|
import { CenterTabStop, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop";
|
||||||
import { NumberProperties } from "./formatting/unordered-list";
|
import { NumberProperties } from "./formatting/unordered-list";
|
||||||
|
import { Hyperlink } from "./links";
|
||||||
import { ParagraphProperties } from "./properties";
|
import { ParagraphProperties } from "./properties";
|
||||||
|
|
||||||
export class Paragraph extends XmlComponent {
|
export class Paragraph extends XmlComponent {
|
||||||
@ -32,6 +33,11 @@ export class Paragraph extends XmlComponent {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addHyperLink(hyperlink: Hyperlink): Paragraph {
|
||||||
|
this.root.push(hyperlink);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public createTextRun(text: string): TextRun {
|
public createTextRun(text: string): TextRun {
|
||||||
const run = new TextRun(text);
|
const run = new TextRun(text);
|
||||||
this.addRun(run);
|
this.addRun(run);
|
||||||
@ -69,6 +75,11 @@ export class Paragraph extends XmlComponent {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public heading6(): Paragraph {
|
||||||
|
this.properties.push(new Style("Heading6"));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public title(): Paragraph {
|
public title(): Paragraph {
|
||||||
this.properties.push(new Style("Title"));
|
this.properties.push(new Style("Title"));
|
||||||
return this;
|
return this;
|
||||||
@ -124,9 +135,9 @@ export class Paragraph extends XmlComponent {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bullet(): Paragraph {
|
public bullet(indentLevel: number = 0): Paragraph {
|
||||||
this.properties.push(new Style("ListParagraph"));
|
this.properties.push(new Style("ListParagraph"));
|
||||||
this.properties.push(new NumberProperties(1, 0));
|
this.properties.push(new NumberProperties(1, indentLevel));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,6 +147,11 @@ export class Paragraph extends XmlComponent {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setCustomNumbering(numberId: number, indentLevel: number): Paragraph {
|
||||||
|
this.properties.push(new NumberProperties(numberId, indentLevel));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public style(styleId: string): Paragraph {
|
public style(styleId: string): Paragraph {
|
||||||
this.properties.push(new Style(styleId));
|
this.properties.push(new Style(styleId));
|
||||||
return this;
|
return this;
|
||||||
|
38
src/file/paragraph/run/page-number.ts
Normal file
38
src/file/paragraph/run/page-number.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
class FidCharAttrs extends XmlAttributeComponent<{ type: "begin" | "end" | "separate" }> {
|
||||||
|
protected xmlKeys = { type: "w:fldCharType" };
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextAttributes extends XmlAttributeComponent<{ space: "default" | "preserve" }> {
|
||||||
|
protected xmlKeys = { space: "xml:space" };
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Begin extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("w:fldChar");
|
||||||
|
this.root.push(new FidCharAttrs({ type: "begin" }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Page extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("w:instrText");
|
||||||
|
this.root.push(new TextAttributes({ space: "preserve" }));
|
||||||
|
this.root.push("PAGE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Separate extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("w:fldChar");
|
||||||
|
this.root.push(new FidCharAttrs({ type: "separate" }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class End extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("w:fldChar");
|
||||||
|
this.root.push(new FidCharAttrs({ type: "end" }));
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,8 @@ 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;
|
||||||
|
|
||||||
constructor(imageData: IMediaData) {
|
constructor(imageData: IMediaData) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@ -10,6 +12,20 @@ export class PictureRun extends Run {
|
|||||||
throw new Error("imageData cannot be undefined");
|
throw new Error("imageData cannot be undefined");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.root.push(new Drawing(imageData));
|
this.drawing = new Drawing(imageData);
|
||||||
|
|
||||||
|
this.root.push(this.drawing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public scale(factorX: number, factorY?: number): void {
|
||||||
|
if (!factorX) {
|
||||||
|
factorX = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!factorY) {
|
||||||
|
factorY = factorX;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.drawing.scale(factorX, factorY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import { Break } from "./break";
|
import { Break } from "./break";
|
||||||
import { Caps, SmallCaps } from "./caps";
|
import { Caps, SmallCaps } from "./caps";
|
||||||
import { Bold, Color, DoubleStrike, Italics, Size, Strike } from "./formatting";
|
import { Bold, Color, DoubleStrike, Italics, Size, Strike } from "./formatting";
|
||||||
|
import { Begin, End, Page, Separate } from "./page-number";
|
||||||
import { RunProperties } from "./properties";
|
import { RunProperties } from "./properties";
|
||||||
import { RunFonts } from "./run-fonts";
|
import { RunFonts } from "./run-fonts";
|
||||||
import { SubScript, SuperScript } from "./script";
|
import { SubScript, SuperScript } from "./script";
|
||||||
@ -55,6 +56,14 @@ export class Run extends XmlComponent {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public pageNumber(): Run {
|
||||||
|
this.root.push(new Begin());
|
||||||
|
this.root.push(new Page());
|
||||||
|
this.root.push(new Separate());
|
||||||
|
this.root.push(new End());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public smallCaps(): Run {
|
public smallCaps(): Run {
|
||||||
this.properties.push(new SmallCaps());
|
this.properties.push(new SmallCaps());
|
||||||
return this;
|
return this;
|
||||||
|
@ -4,6 +4,7 @@ export interface IRelationshipAttributesProperties {
|
|||||||
id: string;
|
id: string;
|
||||||
type: string;
|
type: string;
|
||||||
target: string;
|
target: string;
|
||||||
|
targetMode?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RelationshipAttributes extends XmlAttributeComponent<IRelationshipAttributesProperties> {
|
export class RelationshipAttributes extends XmlAttributeComponent<IRelationshipAttributesProperties> {
|
||||||
@ -11,5 +12,6 @@ export class RelationshipAttributes extends XmlAttributeComponent<IRelationshipA
|
|||||||
id: "Id",
|
id: "Id",
|
||||||
type: "Type",
|
type: "Type",
|
||||||
target: "Target",
|
target: "Target",
|
||||||
|
targetMode: "TargetMode",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,13 @@ export type RelationshipType =
|
|||||||
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"
|
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"
|
||||||
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
|
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
|
||||||
| "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"
|
| "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"
|
||||||
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties";
|
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"
|
||||||
|
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
|
||||||
|
|
||||||
|
export type TargetModeType = "External";
|
||||||
|
|
||||||
export class Relationship extends XmlComponent {
|
export class Relationship extends XmlComponent {
|
||||||
constructor(id: string, type: RelationshipType, target: string) {
|
constructor(id: string, type: RelationshipType, target: string, targetMode?: TargetModeType) {
|
||||||
super("Relationship");
|
super("Relationship");
|
||||||
|
|
||||||
this.root.push(
|
this.root.push(
|
||||||
@ -24,6 +27,7 @@ export class Relationship extends XmlComponent {
|
|||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
target,
|
target,
|
||||||
|
targetMode,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
import { RelationshipsAttributes } from "./attributes";
|
import { RelationshipsAttributes } from "./attributes";
|
||||||
import { Relationship, RelationshipType } from "./relationship/relationship";
|
import { Relationship, RelationshipType, TargetModeType } from "./relationship/relationship";
|
||||||
|
|
||||||
export class Relationships extends XmlComponent {
|
export class Relationships extends XmlComponent {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -16,8 +16,8 @@ export class Relationships extends XmlComponent {
|
|||||||
this.root.push(relationship);
|
this.root.push(relationship);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createRelationship(id: number, type: RelationshipType, target: string): Relationship {
|
public createRelationship(id: number, type: RelationshipType, target: string, targetMode?: TargetModeType): Relationship {
|
||||||
const relationship = new Relationship(`rId${id}`, type, target);
|
const relationship = new Relationship(`rId${id}`, type, target, targetMode);
|
||||||
this.addRelationship(relationship);
|
this.addRelationship(relationship);
|
||||||
|
|
||||||
return relationship;
|
return relationship;
|
||||||
|
159
src/file/styles/external-styles-factory.spec.ts
Normal file
159
src/file/styles/external-styles-factory.spec.ts
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { ExternalStylesFactory } from "./external-styles-factory";
|
||||||
|
|
||||||
|
describe("External styles factory", () => {
|
||||||
|
let externalStyles;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
externalStyles = `
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<w:styles xmlns:mc="first" xmlns:r="second">
|
||||||
|
<w:docDefaults>
|
||||||
|
</w:docDefaults>
|
||||||
|
|
||||||
|
<w:latentStyles w:defLockedState="1" w:defUIPriority="99">
|
||||||
|
</w:latentStyles>
|
||||||
|
|
||||||
|
<w:style w:type="paragraph" w:default="1" w:styleId="Normal">
|
||||||
|
<w:name w:val="Normal"/>
|
||||||
|
<w:qFormat/>
|
||||||
|
</w:style>
|
||||||
|
|
||||||
|
<w:style w:type="paragraph" w:styleId="Heading1">
|
||||||
|
<w:name w:val="heading 1"/>
|
||||||
|
<w:basedOn w:val="Normal"/>
|
||||||
|
<w:pPr>
|
||||||
|
<w:keepNext/>
|
||||||
|
<w:keepLines/>
|
||||||
|
|
||||||
|
<w:pBdr>
|
||||||
|
<w:bottom w:val="single" w:sz="4" w:space="1" w:color="auto"/>
|
||||||
|
</w:pBdr>
|
||||||
|
</w:pPr>
|
||||||
|
</w:style>
|
||||||
|
</w:styles>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#parse", () => {
|
||||||
|
it("should parse w:styles attributes", () => {
|
||||||
|
const importedStyle = new ExternalStylesFactory().newInstance(externalStyles) as any;
|
||||||
|
|
||||||
|
expect(importedStyle.rootKey).to.equal("w:styles");
|
||||||
|
expect(importedStyle.root[0]._attr).to.eql({
|
||||||
|
"xmlns:mc": "first",
|
||||||
|
"xmlns:r": "second",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse other child elements of w:styles", () => {
|
||||||
|
const importedStyle = new ExternalStylesFactory().newInstance(externalStyles) as any;
|
||||||
|
|
||||||
|
expect(importedStyle.root.length).to.equal(5);
|
||||||
|
expect(importedStyle.root[1]).to.eql({
|
||||||
|
deleted: false,
|
||||||
|
root: [],
|
||||||
|
rootKey: "w:docDefaults",
|
||||||
|
});
|
||||||
|
expect(importedStyle.root[2]).to.eql({
|
||||||
|
_attr: {
|
||||||
|
"w:defLockedState": "1",
|
||||||
|
"w:defUIPriority": "99",
|
||||||
|
},
|
||||||
|
deleted: false,
|
||||||
|
root: [],
|
||||||
|
rootKey: "w:latentStyles",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should parse styles elements", () => {
|
||||||
|
const importedStyle = new ExternalStylesFactory().newInstance(externalStyles) as any;
|
||||||
|
|
||||||
|
expect(importedStyle.root.length).to.equal(5);
|
||||||
|
expect(importedStyle.root[3]).to.eql({
|
||||||
|
_attr: {
|
||||||
|
"w:default": "1",
|
||||||
|
"w:styleId": "Normal",
|
||||||
|
"w:type": "paragraph",
|
||||||
|
},
|
||||||
|
deleted: false,
|
||||||
|
root: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:val": "Normal",
|
||||||
|
},
|
||||||
|
deleted: false,
|
||||||
|
root: [],
|
||||||
|
rootKey: "w:name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deleted: false,
|
||||||
|
root: [],
|
||||||
|
rootKey: "w:qFormat",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rootKey: "w:style",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(importedStyle.root[4]).to.eql({
|
||||||
|
_attr: {
|
||||||
|
"w:styleId": "Heading1",
|
||||||
|
"w:type": "paragraph",
|
||||||
|
},
|
||||||
|
deleted: false,
|
||||||
|
root: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:val": "heading 1",
|
||||||
|
},
|
||||||
|
deleted: false,
|
||||||
|
root: [],
|
||||||
|
rootKey: "w:name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:val": "Normal",
|
||||||
|
},
|
||||||
|
deleted: false,
|
||||||
|
root: [],
|
||||||
|
rootKey: "w:basedOn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deleted: false,
|
||||||
|
root: [
|
||||||
|
{
|
||||||
|
deleted: false,
|
||||||
|
root: [],
|
||||||
|
rootKey: "w:keepNext",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deleted: false,
|
||||||
|
root: [],
|
||||||
|
rootKey: "w:keepLines",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deleted: false,
|
||||||
|
root: [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "auto",
|
||||||
|
"w:space": "1",
|
||||||
|
"w:sz": "4",
|
||||||
|
"w:val": "single",
|
||||||
|
},
|
||||||
|
deleted: false,
|
||||||
|
root: [],
|
||||||
|
rootKey: "w:bottom",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rootKey: "w:pBdr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rootKey: "w:pPr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rootKey: "w:style",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
64
src/file/styles/external-styles-factory.ts
Normal file
64
src/file/styles/external-styles-factory.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import * as fastXmlParser from "fast-xml-parser";
|
||||||
|
|
||||||
|
import { Styles } from "./";
|
||||||
|
import { ImportedRootElementAttributes, ImportedXmlComponent } from "./../../file/xml-components";
|
||||||
|
|
||||||
|
const parseOptions = {
|
||||||
|
ignoreAttributes: false,
|
||||||
|
attributeNamePrefix: "",
|
||||||
|
attrNodeName: "_attr",
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ExternalStylesFactory {
|
||||||
|
/**
|
||||||
|
* Creates new Style based on the given styles.
|
||||||
|
* Parses the styles and convert them to XmlComponent.
|
||||||
|
* Example content from styles.xml:
|
||||||
|
* <?xml version="1.0">
|
||||||
|
* <w:styles xmlns:mc="some schema" ...>
|
||||||
|
*
|
||||||
|
* <w:style w:type="paragraph" w:styleId="Heading1">
|
||||||
|
* <w:name w:val="heading 1"/>
|
||||||
|
* .....
|
||||||
|
* </w:style>
|
||||||
|
*
|
||||||
|
* <w:style w:type="paragraph" w:styleId="Heading2">
|
||||||
|
* <w:name w:val="heading 2"/>
|
||||||
|
* .....
|
||||||
|
* </w:style>
|
||||||
|
*
|
||||||
|
* <w:docDefaults>Or any other element will be parsed to</w:docDefaults>
|
||||||
|
*
|
||||||
|
* </w:styles>
|
||||||
|
* @param externalStyles context from styles.xml
|
||||||
|
*/
|
||||||
|
public newInstance(externalStyles: string): Styles {
|
||||||
|
const xmlStyles = fastXmlParser.parse(externalStyles, parseOptions)["w:styles"];
|
||||||
|
// create styles with attributes from the parsed xml
|
||||||
|
const importedStyle = new Styles(new ImportedRootElementAttributes(xmlStyles._attr));
|
||||||
|
|
||||||
|
// convert other elements (not styles definitions, but default styles and so on ...)
|
||||||
|
Object.keys(xmlStyles)
|
||||||
|
.filter((element) => element !== "_attr" && element !== "w:style")
|
||||||
|
.forEach((element) => {
|
||||||
|
importedStyle.push(new ImportedXmlComponent(element, xmlStyles[element]._attr));
|
||||||
|
});
|
||||||
|
|
||||||
|
// convert the styles one by one
|
||||||
|
xmlStyles["w:style"].map((style) => this.convertElement("w:style", style)).forEach(importedStyle.push.bind(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,7 +1,7 @@
|
|||||||
|
import { DocumentAttributes } from "../document/document-attributes";
|
||||||
import { Color, Italics, Size } from "../paragraph/run/formatting";
|
import { Color, Italics, Size } from "../paragraph/run/formatting";
|
||||||
|
|
||||||
import { Styles } from "./";
|
import { Styles } from "./";
|
||||||
// import { DocumentDefaults } from "./defaults";
|
|
||||||
import {
|
import {
|
||||||
Heading1Style,
|
Heading1Style,
|
||||||
Heading2Style,
|
Heading2Style,
|
||||||
@ -9,13 +9,22 @@ import {
|
|||||||
Heading4Style,
|
Heading4Style,
|
||||||
Heading5Style,
|
Heading5Style,
|
||||||
Heading6Style,
|
Heading6Style,
|
||||||
|
HyperlinkStyle,
|
||||||
ListParagraph,
|
ListParagraph,
|
||||||
TitleStyle,
|
TitleStyle,
|
||||||
} from "./style";
|
} from "./style";
|
||||||
|
|
||||||
export class DefaultStylesFactory {
|
export class DefaultStylesFactory {
|
||||||
public newInstance(): Styles {
|
public newInstance(): Styles {
|
||||||
const styles = new Styles();
|
const documentAttributes = new DocumentAttributes({
|
||||||
|
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
||||||
|
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
||||||
|
w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
||||||
|
w14: "http://schemas.microsoft.com/office/word/2010/wordml",
|
||||||
|
w15: "http://schemas.microsoft.com/office/word/2012/wordml",
|
||||||
|
Ignorable: "w14 w15",
|
||||||
|
});
|
||||||
|
const styles = new Styles(documentAttributes);
|
||||||
styles.createDocumentDefaults();
|
styles.createDocumentDefaults();
|
||||||
|
|
||||||
const titleStyle = new TitleStyle();
|
const titleStyle = new TitleStyle();
|
||||||
@ -54,6 +63,8 @@ export class DefaultStylesFactory {
|
|||||||
// listParagraph.addParagraphProperty();
|
// listParagraph.addParagraphProperty();
|
||||||
styles.push(listParagraph);
|
styles.push(listParagraph);
|
||||||
|
|
||||||
|
const hyperLinkStyle = new HyperlinkStyle();
|
||||||
|
styles.push(hyperLinkStyle);
|
||||||
return styles;
|
return styles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,13 @@
|
|||||||
import { XmlComponent } from "file/xml-components";
|
import { BaseXmlComponent, XmlComponent } from "file/xml-components";
|
||||||
import { DocumentAttributes } from "../document/document-attributes";
|
|
||||||
import { DocumentDefaults } from "./defaults";
|
import { DocumentDefaults } from "./defaults";
|
||||||
import { ParagraphStyle } from "./style";
|
import { ParagraphStyle } from "./style";
|
||||||
|
|
||||||
export class Styles extends XmlComponent {
|
export class Styles extends XmlComponent {
|
||||||
constructor() {
|
constructor(initialStyles?: BaseXmlComponent) {
|
||||||
super("w:styles");
|
super("w:styles");
|
||||||
this.root.push(
|
if (initialStyles) {
|
||||||
new DocumentAttributes({
|
this.root.push(initialStyles);
|
||||||
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
}
|
||||||
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
|
||||||
w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
|
||||||
w14: "http://schemas.microsoft.com/office/word/2010/wordml",
|
|
||||||
w15: "http://schemas.microsoft.com/office/word/2012/wordml",
|
|
||||||
Ignorable: "w14 w15",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public push(style: XmlComponent): Styles {
|
public push(style: XmlComponent): Styles {
|
||||||
|
@ -3,7 +3,7 @@ import * as paragraph from "../../paragraph";
|
|||||||
import * as formatting from "../../paragraph/run/formatting";
|
import * as formatting from "../../paragraph/run/formatting";
|
||||||
import { RunProperties } from "../../paragraph/run/properties";
|
import { RunProperties } from "../../paragraph/run/properties";
|
||||||
|
|
||||||
import { BasedOn, Name, Next, QuickFormat } from "./components";
|
import { BasedOn, Name, Next, QuickFormat, UiPriority, UnhideWhenUsed } from "./components";
|
||||||
|
|
||||||
export interface IStyleAttributes {
|
export interface IStyleAttributes {
|
||||||
type?: string;
|
type?: string;
|
||||||
@ -249,3 +249,43 @@ export class ListParagraph extends ParagraphStyle {
|
|||||||
this.root.push(new QuickFormat());
|
this.root.push(new QuickFormat());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class CharacterStyle extends Style {
|
||||||
|
private readonly runProperties: RunProperties;
|
||||||
|
|
||||||
|
constructor(styleId: string, name?: string) {
|
||||||
|
super({ type: "character", styleId: styleId }, name);
|
||||||
|
this.runProperties = new RunProperties();
|
||||||
|
this.root.push(this.runProperties);
|
||||||
|
this.root.push(new UiPriority("99"));
|
||||||
|
this.root.push(new UnhideWhenUsed(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
public basedOn(parentId: string): CharacterStyle {
|
||||||
|
this.root.push(new BasedOn(parentId));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addRunProperty(property: XmlComponent): void {
|
||||||
|
this.runProperties.push(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
public color(color: string): CharacterStyle {
|
||||||
|
this.addRunProperty(new formatting.Color(color));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public underline(underlineType?: string, color?: string): CharacterStyle {
|
||||||
|
this.addRunProperty(new formatting.Underline(underlineType, color));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HyperlinkStyle extends CharacterStyle {
|
||||||
|
constructor() {
|
||||||
|
super("Hyperlink", "Hyperlink");
|
||||||
|
this.basedOn("DefaultParagraphFont")
|
||||||
|
.color("0563C1")
|
||||||
|
.underline("single");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from "./table";
|
export * from "./table";
|
||||||
|
export * from "./table-cell";
|
||||||
|
@ -16,6 +16,11 @@ export class TableProperties extends XmlComponent {
|
|||||||
this.root.push(new TableLayout("fixed"));
|
this.root.push(new TableLayout("fixed"));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setBorder(): TableProperties {
|
||||||
|
this.root.push(new TableBorders());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ITableWidth {
|
interface ITableWidth {
|
||||||
@ -46,3 +51,38 @@ class TableLayout extends XmlComponent {
|
|||||||
this.root.push(new TableLayoutAttributes({ type }));
|
this.root.push(new TableLayoutAttributes({ type }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TableBorders extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("w:tblBorders");
|
||||||
|
this.root.push(new TableBordersElement("w:top", "single", 4, 0, "auto"));
|
||||||
|
this.root.push(new TableBordersElement("w:left", "single", 4, 0, "auto"));
|
||||||
|
this.root.push(new TableBordersElement("w:bottom", "single", 4, 0, "auto"));
|
||||||
|
this.root.push(new TableBordersElement("w:right", "single", 4, 0, "auto"));
|
||||||
|
this.root.push(new TableBordersElement("w:insideH", "single", 4, 0, "auto"));
|
||||||
|
this.root.push(new TableBordersElement("w:insideV", "single", 4, 0, "auto"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TableBordersElement extends XmlComponent {
|
||||||
|
constructor(elementName: string, value: string, size: number, space: number, color: string) {
|
||||||
|
super(elementName);
|
||||||
|
this.root.push(
|
||||||
|
new TableBordersAttributes({
|
||||||
|
value,
|
||||||
|
size,
|
||||||
|
space,
|
||||||
|
color,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TableBordersAttributes extends XmlAttributeComponent<{ value: string; size: number; space: number; color: string }> {
|
||||||
|
protected xmlKeys = {
|
||||||
|
value: "w:val",
|
||||||
|
size: "w:sz",
|
||||||
|
space: "w:space",
|
||||||
|
color: "w:color",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
181
src/file/table/table-cell.spec.ts
Normal file
181
src/file/table/table-cell.spec.ts
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
|
||||||
|
import { TableCellBorders, BorderStyle, TableCellWidth, WidthType } from "./table-cell";
|
||||||
|
import { Formatter } from "../../export/formatter";
|
||||||
|
|
||||||
|
describe("TableCellBorders", () => {
|
||||||
|
describe("#prepForXml", () => {
|
||||||
|
it("should not add empty borders element if there are no borders defined", () => {
|
||||||
|
const tb = new TableCellBorders();
|
||||||
|
const tree = new Formatter().format(tb);
|
||||||
|
expect(tree).to.deep.equal("");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#addingBorders", () => {
|
||||||
|
it("should add top border", () => {
|
||||||
|
const tb = new TableCellBorders();
|
||||||
|
tb.addTopBorder(BorderStyle.DOTTED, 1, "FF00FF");
|
||||||
|
|
||||||
|
const tree = new Formatter().format(tb);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:tcBorders": [
|
||||||
|
{
|
||||||
|
"w:top": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "FF00FF",
|
||||||
|
"w:sz": 1,
|
||||||
|
"w:val": "dotted",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add start(left) border", () => {
|
||||||
|
const tb = new TableCellBorders();
|
||||||
|
tb.addStartBorder(BorderStyle.SINGLE, 2, "FF00FF");
|
||||||
|
|
||||||
|
const tree = new Formatter().format(tb);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:tcBorders": [
|
||||||
|
{
|
||||||
|
"w:start": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "FF00FF",
|
||||||
|
"w:sz": 2,
|
||||||
|
"w:val": "single",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add bottom border", () => {
|
||||||
|
const tb = new TableCellBorders();
|
||||||
|
tb.addBottomBorder(BorderStyle.DOUBLE, 1, "FF00FF");
|
||||||
|
|
||||||
|
const tree = new Formatter().format(tb);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:tcBorders": [
|
||||||
|
{
|
||||||
|
"w:bottom": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "FF00FF",
|
||||||
|
"w:sz": 1,
|
||||||
|
"w:val": "double",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add end(right) border", () => {
|
||||||
|
const tb = new TableCellBorders();
|
||||||
|
tb.addEndBorder(BorderStyle.THICK, 3, "FF00FF");
|
||||||
|
|
||||||
|
const tree = new Formatter().format(tb);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:tcBorders": [
|
||||||
|
{
|
||||||
|
"w:end": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "FF00FF",
|
||||||
|
"w:sz": 3,
|
||||||
|
"w:val": "thick",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add multiple borders", () => {
|
||||||
|
const tb = new TableCellBorders();
|
||||||
|
tb.addTopBorder(BorderStyle.DOTTED, 1, "FF00FF");
|
||||||
|
tb.addEndBorder(BorderStyle.THICK, 3, "FF00FF");
|
||||||
|
tb.addBottomBorder(BorderStyle.DOUBLE, 1, "FF00FF");
|
||||||
|
tb.addStartBorder(BorderStyle.SINGLE, 2, "FF00FF");
|
||||||
|
|
||||||
|
const tree = new Formatter().format(tb);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:tcBorders": [
|
||||||
|
{
|
||||||
|
"w:top": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "FF00FF",
|
||||||
|
"w:sz": 1,
|
||||||
|
"w:val": "dotted",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:end": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "FF00FF",
|
||||||
|
"w:sz": 3,
|
||||||
|
"w:val": "thick",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:bottom": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "FF00FF",
|
||||||
|
"w:sz": 1,
|
||||||
|
"w:val": "double",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:start": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "FF00FF",
|
||||||
|
"w:sz": 2,
|
||||||
|
"w:val": "single",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("TableCellWidth", () => {
|
||||||
|
describe("#constructor", () => {
|
||||||
|
it("should create object", () => {
|
||||||
|
const tcWidth = new TableCellWidth(100, WidthType.DXA);
|
||||||
|
const tree = new Formatter().format(tcWidth);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"w:tcW": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:type": "dxa",
|
||||||
|
"w:w": 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
207
src/file/table/table-cell.ts
Normal file
207
src/file/table/table-cell.ts
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
import { IXmlableObject, XmlAttributeComponent, XmlComponent } from "file/xml-components";
|
||||||
|
|
||||||
|
export enum BorderStyle {
|
||||||
|
SINGLE = "single",
|
||||||
|
DASH_DOT_STROKED = "dashDotStroked",
|
||||||
|
DASHED = "dashed",
|
||||||
|
DASH_SMALL_GAP = "dashSmallGap",
|
||||||
|
DOT_DASH = "dotDash",
|
||||||
|
DOT_DOT_DASH = "dotDotDash",
|
||||||
|
DOTTED = "dotted",
|
||||||
|
DOUBLE = "double",
|
||||||
|
DOUBLE_WAVE = "doubleWave",
|
||||||
|
INSET = "inset",
|
||||||
|
NIL = "nil",
|
||||||
|
NONE = "none",
|
||||||
|
OUTSET = "outset",
|
||||||
|
THICK = "thick",
|
||||||
|
THICK_THIN_LARGE_GAP = "thickThinLargeGap",
|
||||||
|
THICK_THIN_MEDIUM_GAP = "thickThinMediumGap",
|
||||||
|
THICK_THIN_SMALL_GAP = "thickThinSmallGap",
|
||||||
|
THIN_THICK_LARGE_GAP = "thinThickLargeGap",
|
||||||
|
THIN_THICK_MEDIUM_GAP = "thinThickMediumGap",
|
||||||
|
THIN_THICK_SMALL_GAP = "thinThickSmallGap",
|
||||||
|
THIN_THICK_THIN_LARGE_GAP = "thinThickThinLargeGap",
|
||||||
|
THIN_THICK_THIN_MEDIUM_GAP = "thinThickThinMediumGap",
|
||||||
|
THIN_THICK_THIN_SMALL_GAP = "thinThickThinSmallGap",
|
||||||
|
THREE_D_EMBOSS = "threeDEmboss",
|
||||||
|
THREE_D_ENGRAVE = "threeDEngrave",
|
||||||
|
TRIPLE = "triple",
|
||||||
|
WAVE = "wave",
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICellBorder {
|
||||||
|
style: BorderStyle;
|
||||||
|
size: number;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CellBorderAttributes extends XmlAttributeComponent<ICellBorder> {
|
||||||
|
protected xmlKeys = { style: "w:val", size: "w:sz", color: "w:color" };
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseTableCellBorder extends XmlComponent {
|
||||||
|
public setProperties(style: BorderStyle, size: number, color: string): BaseTableCellBorder {
|
||||||
|
const attrs = new CellBorderAttributes({
|
||||||
|
style: style,
|
||||||
|
size: size,
|
||||||
|
color: color,
|
||||||
|
});
|
||||||
|
this.root.push(attrs);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TableCellBorders extends XmlComponent {
|
||||||
|
constructor() {
|
||||||
|
super("w:tcBorders");
|
||||||
|
}
|
||||||
|
|
||||||
|
public prepForXml(): IXmlableObject {
|
||||||
|
return this.root.length > 0 ? super.prepForXml() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public addTopBorder(style: BorderStyle, size: number, color: string): TableCellBorders {
|
||||||
|
const top = new BaseTableCellBorder("w:top");
|
||||||
|
top.setProperties(style, size, color);
|
||||||
|
this.root.push(top);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addStartBorder(style: BorderStyle, size: number, color: string): TableCellBorders {
|
||||||
|
const start = new BaseTableCellBorder("w:start");
|
||||||
|
start.setProperties(style, size, color);
|
||||||
|
this.root.push(start);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addBottomBorder(style: BorderStyle, size: number, color: string): TableCellBorders {
|
||||||
|
const bottom = new BaseTableCellBorder("w:bottom");
|
||||||
|
bottom.setProperties(style, size, color);
|
||||||
|
this.root.push(bottom);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addEndBorder(style: BorderStyle, size: number, color: string): TableCellBorders {
|
||||||
|
const end = new BaseTableCellBorder("w:end");
|
||||||
|
end.setProperties(style, size, color);
|
||||||
|
this.root.push(end);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attributes fot the GridSpan element.
|
||||||
|
*/
|
||||||
|
class GridSpanAttributes extends XmlAttributeComponent<{ val: number }> {
|
||||||
|
protected xmlKeys = { val: "w:val" };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GridSpan element. Should be used in a table cell. Pass the number of columns that this cell need to span.
|
||||||
|
*/
|
||||||
|
export class GridSpan extends XmlComponent {
|
||||||
|
constructor(value: number) {
|
||||||
|
super("w:gridSpan");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new GridSpanAttributes({
|
||||||
|
val: value,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertical merge types.
|
||||||
|
*/
|
||||||
|
export enum VMergeType {
|
||||||
|
/**
|
||||||
|
* Cell that is merged with upper one.
|
||||||
|
*/
|
||||||
|
CONTINUE = "continue",
|
||||||
|
/**
|
||||||
|
* Cell that is starting the vertical merge.
|
||||||
|
*/
|
||||||
|
RESTART = "restart",
|
||||||
|
}
|
||||||
|
|
||||||
|
class VMergeAttributes extends XmlAttributeComponent<{ val: VMergeType }> {
|
||||||
|
protected xmlKeys = { val: "w:val" };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertical merge element. Should be used in a table cell.
|
||||||
|
*/
|
||||||
|
export class VMerge extends XmlComponent {
|
||||||
|
constructor(value: VMergeType) {
|
||||||
|
super("w:vMerge");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new VMergeAttributes({
|
||||||
|
val: value,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum VerticalAlign {
|
||||||
|
BOTTOM = "bottom",
|
||||||
|
CENTER = "center",
|
||||||
|
TOP = "top",
|
||||||
|
}
|
||||||
|
|
||||||
|
class VAlignAttributes extends XmlAttributeComponent<{ val: VerticalAlign }> {
|
||||||
|
protected xmlKeys = { val: "w:val" };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertical align element.
|
||||||
|
*/
|
||||||
|
export class VAlign extends XmlComponent {
|
||||||
|
constructor(value: VerticalAlign) {
|
||||||
|
super("w:vAlign");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new VAlignAttributes({
|
||||||
|
val: value,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum WidthType {
|
||||||
|
/** Auto. */
|
||||||
|
AUTO = "auto",
|
||||||
|
/** Value is in twentieths of a point */
|
||||||
|
DXA = "dxa",
|
||||||
|
/** No (empty) value. */
|
||||||
|
NIL = "nil",
|
||||||
|
/** Value is in percentage. */
|
||||||
|
PERCENTAGE = "pct",
|
||||||
|
}
|
||||||
|
|
||||||
|
class TableCellWidthAttributes extends XmlAttributeComponent<{ type: WidthType; width: string | number }> {
|
||||||
|
protected xmlKeys = { width: "w:w", type: "w:type" };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table cell width element.
|
||||||
|
*/
|
||||||
|
export class TableCellWidth extends XmlComponent {
|
||||||
|
constructor(value: string | number, type: WidthType) {
|
||||||
|
super("w:tcW");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new TableCellWidthAttributes({
|
||||||
|
width: value,
|
||||||
|
type: type,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,83 @@ import { Formatter } from "../../export/formatter";
|
|||||||
import { Paragraph } from "../paragraph";
|
import { Paragraph } from "../paragraph";
|
||||||
import { Table } from "./";
|
import { Table } from "./";
|
||||||
|
|
||||||
|
const DEFAULT_TABLE_PROPERTIES = {
|
||||||
|
"w:tblBorders": [
|
||||||
|
{
|
||||||
|
"w:top": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "auto",
|
||||||
|
"w:space": 0,
|
||||||
|
"w:sz": 4,
|
||||||
|
"w:val": "single",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:left": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "auto",
|
||||||
|
"w:space": 0,
|
||||||
|
"w:sz": 4,
|
||||||
|
"w:val": "single",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:bottom": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "auto",
|
||||||
|
"w:space": 0,
|
||||||
|
"w:sz": 4,
|
||||||
|
"w:val": "single",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:right": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "auto",
|
||||||
|
"w:space": 0,
|
||||||
|
"w:sz": 4,
|
||||||
|
"w:val": "single",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:insideH": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "auto",
|
||||||
|
"w:space": 0,
|
||||||
|
"w:sz": 4,
|
||||||
|
"w:val": "single",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:insideV": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
"w:color": "auto",
|
||||||
|
"w:space": 0,
|
||||||
|
"w:sz": 4,
|
||||||
|
"w:val": "single",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
describe("Table", () => {
|
describe("Table", () => {
|
||||||
describe("#constructor", () => {
|
describe("#constructor", () => {
|
||||||
it("creates a table with the correct number of rows and columns", () => {
|
it("creates a table with the correct number of rows and columns", () => {
|
||||||
@ -13,7 +90,7 @@ describe("Table", () => {
|
|||||||
const cell = { "w:tc": [{ "w:tcPr": [] }, { "w:p": [{ "w:pPr": [] }] }] };
|
const cell = { "w:tc": [{ "w:tcPr": [] }, { "w:p": [{ "w:pPr": [] }] }] };
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:tbl": [
|
"w:tbl": [
|
||||||
{ "w:tblPr": [] },
|
{ "w:tblPr": [DEFAULT_TABLE_PROPERTIES] },
|
||||||
{
|
{
|
||||||
"w:tblGrid": [{ "w:gridCol": [{ _attr: { "w:w": 1 } }] }, { "w:gridCol": [{ _attr: { "w:w": 1 } }] }],
|
"w:tblGrid": [{ "w:gridCol": [{ _attr: { "w:w": 1 } }] }, { "w:gridCol": [{ _attr: { "w:w": 1 } }] }],
|
||||||
},
|
},
|
||||||
@ -55,7 +132,7 @@ describe("Table", () => {
|
|||||||
});
|
});
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:tbl": [
|
"w:tbl": [
|
||||||
{ "w:tblPr": [] },
|
{ "w:tblPr": [DEFAULT_TABLE_PROPERTIES] },
|
||||||
{
|
{
|
||||||
"w:tblGrid": [{ "w:gridCol": [{ _attr: { "w:w": 1 } }] }, { "w:gridCol": [{ _attr: { "w:w": 1 } }] }],
|
"w:tblGrid": [{ "w:gridCol": [{ _attr: { "w:w": 1 } }] }, { "w:gridCol": [{ _attr: { "w:w": 1 } }] }],
|
||||||
},
|
},
|
||||||
@ -84,7 +161,7 @@ describe("Table", () => {
|
|||||||
});
|
});
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:tbl": [
|
"w:tbl": [
|
||||||
{ "w:tblPr": [] },
|
{ "w:tblPr": [DEFAULT_TABLE_PROPERTIES] },
|
||||||
{
|
{
|
||||||
"w:tblGrid": [{ "w:gridCol": [{ _attr: { "w:w": 1 } }] }, { "w:gridCol": [{ _attr: { "w:w": 1 } }] }],
|
"w:tblGrid": [{ "w:gridCol": [{ _attr: { "w:w": 1 } }] }, { "w:gridCol": [{ _attr: { "w:w": 1 } }] }],
|
||||||
},
|
},
|
||||||
@ -104,7 +181,7 @@ describe("Table", () => {
|
|||||||
.which.is.an("array")
|
.which.is.an("array")
|
||||||
.with.has.length.at.least(1);
|
.with.has.length.at.least(1);
|
||||||
expect(tree["w:tbl"][0]).to.deep.equal({
|
expect(tree["w:tbl"][0]).to.deep.equal({
|
||||||
"w:tblPr": [{ "w:tblW": [{ _attr: { "w:type": "pct", "w:w": 1000 } }] }],
|
"w:tblPr": [DEFAULT_TABLE_PROPERTIES, { "w:tblW": [{ _attr: { "w:type": "pct", "w:w": 1000 } }] }],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -118,7 +195,7 @@ describe("Table", () => {
|
|||||||
.which.is.an("array")
|
.which.is.an("array")
|
||||||
.with.has.length.at.least(1);
|
.with.has.length.at.least(1);
|
||||||
expect(tree["w:tbl"][0]).to.deep.equal({
|
expect(tree["w:tbl"][0]).to.deep.equal({
|
||||||
"w:tblPr": [{ "w:tblLayout": [{ _attr: { "w:type": "fixed" } }] }],
|
"w:tblPr": [DEFAULT_TABLE_PROPERTIES, { "w:tblLayout": [{ _attr: { "w:type": "fixed" } }] }],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { GridSpan, TableCellBorders, TableCellWidth, VAlign, VerticalAlign, VMerge, VMergeType, WidthType } from "file/table/table-cell";
|
||||||
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
import { IXmlableObject, XmlComponent } from "file/xml-components";
|
||||||
import { Paragraph } from "../paragraph";
|
import { Paragraph } from "../paragraph";
|
||||||
import { TableGrid } from "./grid";
|
import { TableGrid } from "./grid";
|
||||||
@ -8,26 +9,32 @@ export class Table extends XmlComponent {
|
|||||||
private readonly rows: TableRow[];
|
private readonly rows: TableRow[];
|
||||||
private readonly grid: TableGrid;
|
private readonly grid: TableGrid;
|
||||||
|
|
||||||
constructor(rows: number, cols: number) {
|
constructor(rows: number, cols: number, colSizes?: number[]) {
|
||||||
super("w:tbl");
|
super("w:tbl");
|
||||||
this.properties = new TableProperties();
|
this.properties = new TableProperties();
|
||||||
this.root.push(this.properties);
|
this.root.push(this.properties);
|
||||||
|
this.properties.setBorder();
|
||||||
|
|
||||||
const gridCols: number[] = [];
|
if (colSizes && colSizes.length > 0) {
|
||||||
for (let i = 0; i < cols; i++) {
|
this.grid = new TableGrid(colSizes);
|
||||||
/*
|
} else {
|
||||||
0-width columns don't get rendered correctly, so we need
|
const gridCols: number[] = [];
|
||||||
to give them some value. A reasonable default would be
|
for (let i = 0; i < cols; i++) {
|
||||||
~6in / numCols, but if we do that it becomes very hard
|
/*
|
||||||
to resize the table using setWidth, unless the layout
|
0-width columns don't get rendered correctly, so we need
|
||||||
algorithm is set to 'fixed'. Instead, the approach here
|
to give them some value. A reasonable default would be
|
||||||
means even in 'auto' layout, setting a width on the
|
~6in / numCols, but if we do that it becomes very hard
|
||||||
table will make it look reasonable, as the layout
|
to resize the table using setWidth, unless the layout
|
||||||
algorithm will expand columns to fit its content
|
algorithm is set to 'fixed'. Instead, the approach here
|
||||||
*/
|
means even in 'auto' layout, setting a width on the
|
||||||
gridCols.push(1);
|
table will make it look reasonable, as the layout
|
||||||
|
algorithm will expand columns to fit its content
|
||||||
|
*/
|
||||||
|
gridCols.push(1);
|
||||||
|
}
|
||||||
|
this.grid = new TableGrid(gridCols);
|
||||||
}
|
}
|
||||||
this.grid = new TableGrid(gridCols);
|
|
||||||
this.root.push(this.grid);
|
this.root.push(this.grid);
|
||||||
|
|
||||||
this.rows = [];
|
this.rows = [];
|
||||||
@ -111,10 +118,45 @@ export class TableCell extends XmlComponent {
|
|||||||
this.addContent(para);
|
this.addContent(para);
|
||||||
return para;
|
return para;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get cellProperties(): TableCellProperties {
|
||||||
|
return this.properties;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TableCellProperties extends XmlComponent {
|
export class TableCellProperties extends XmlComponent {
|
||||||
|
private cellBorder: TableCellBorders;
|
||||||
constructor() {
|
constructor() {
|
||||||
super("w:tcPr");
|
super("w:tcPr");
|
||||||
|
this.cellBorder = new TableCellBorders();
|
||||||
|
this.root.push(this.cellBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
get borders(): TableCellBorders {
|
||||||
|
return this.cellBorder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addGridSpan(cellSpan: number): TableCellProperties {
|
||||||
|
this.root.push(new GridSpan(cellSpan));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addVerticalMerge(type: VMergeType): TableCellProperties {
|
||||||
|
this.root.push(new VMerge(type));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setVerticalAlign(vAlignType: VerticalAlign): TableCellProperties {
|
||||||
|
this.root.push(new VAlign(vAlignType));
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setWidth(width: string | number, type: WidthType): TableCellProperties {
|
||||||
|
this.root.push(new TableCellWidth(width, type));
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,15 @@ import { IXmlableObject } from "./xmlable-object";
|
|||||||
|
|
||||||
export abstract class BaseXmlComponent {
|
export abstract class BaseXmlComponent {
|
||||||
protected rootKey: string;
|
protected rootKey: string;
|
||||||
|
protected deleted: boolean = false;
|
||||||
|
|
||||||
constructor(rootKey: string) {
|
constructor(rootKey: string) {
|
||||||
this.rootKey = rootKey;
|
this.rootKey = rootKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract prepForXml(): IXmlableObject;
|
public abstract prepForXml(): IXmlableObject;
|
||||||
|
|
||||||
|
public get isDeleted(): boolean {
|
||||||
|
return this.deleted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,4 +23,8 @@ export abstract class XmlAttributeComponent<T> extends BaseXmlComponent {
|
|||||||
});
|
});
|
||||||
return { _attr: attrs };
|
return { _attr: attrs };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public set(properties: T): void {
|
||||||
|
this.root = properties;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
34
src/file/xml-components/imported-xml-component.spec.ts
Normal file
34
src/file/xml-components/imported-xml-component.spec.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { expect } from "chai";
|
||||||
|
import { ImportedXmlComponent } from "./";
|
||||||
|
|
||||||
|
describe("ImportedXmlComponent", () => {
|
||||||
|
let importedXmlComponent: ImportedXmlComponent;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const attributes = {
|
||||||
|
someAttr: "1",
|
||||||
|
otherAttr: "2",
|
||||||
|
};
|
||||||
|
importedXmlComponent = new ImportedXmlComponent("w:test", attributes);
|
||||||
|
importedXmlComponent.push(new ImportedXmlComponent("w:child"));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#prepForXml()", () => {
|
||||||
|
it("should transform for xml", () => {
|
||||||
|
const converted = importedXmlComponent.prepForXml();
|
||||||
|
expect(converted).to.eql({
|
||||||
|
"w:test": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
someAttr: "1",
|
||||||
|
otherAttr: "2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"w:child": [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
74
src/file/xml-components/imported-xml-component.ts
Normal file
74
src/file/xml-components/imported-xml-component.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// tslint:disable:no-any
|
||||||
|
// tslint:disable:variable-name
|
||||||
|
import { IXmlableObject, XmlComponent } from "./";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents imported xml component from xml file.
|
||||||
|
*/
|
||||||
|
export class ImportedXmlComponent extends XmlComponent {
|
||||||
|
private _attr: any;
|
||||||
|
|
||||||
|
constructor(rootKey: string, attr?: any) {
|
||||||
|
super(rootKey);
|
||||||
|
|
||||||
|
if (attr) {
|
||||||
|
this._attr = attr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms the object so it can be converted to xml. Example:
|
||||||
|
* <w:someKey someAttr="1" otherAttr="11">
|
||||||
|
* <w:child childAttr="2">
|
||||||
|
* </w:child>
|
||||||
|
* </w:someKey>
|
||||||
|
* {
|
||||||
|
* 'w:someKey': [
|
||||||
|
* {
|
||||||
|
* _attr: {
|
||||||
|
* someAttr: "1",
|
||||||
|
* otherAttr: "11"
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* 'w:child': [
|
||||||
|
* {
|
||||||
|
* _attr: {
|
||||||
|
* childAttr: "2"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
public prepForXml(): IXmlableObject {
|
||||||
|
const result = super.prepForXml();
|
||||||
|
if (!!this._attr) {
|
||||||
|
if (!Array.isArray(result[this.rootKey])) {
|
||||||
|
result[this.rootKey] = [result[this.rootKey]];
|
||||||
|
}
|
||||||
|
result[this.rootKey].unshift({ _attr: this._attr });
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public push(xmlComponent: XmlComponent): void {
|
||||||
|
this.root.push(xmlComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for the attributes of root element that is being imported.
|
||||||
|
*/
|
||||||
|
export class ImportedRootElementAttributes extends XmlComponent {
|
||||||
|
constructor(private _attr: any) {
|
||||||
|
super("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public prepForXml(): IXmlableObject {
|
||||||
|
return {
|
||||||
|
_attr: this._attr,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
export * from "./xml-component";
|
export * from "./xml-component";
|
||||||
export * from "./attributes";
|
export * from "./attributes";
|
||||||
export * from "./default-attributes";
|
export * from "./default-attributes";
|
||||||
|
export * from "./imported-xml-component";
|
||||||
export * from "./xmlable-object";
|
export * from "./xmlable-object";
|
||||||
|
@ -18,4 +18,15 @@ describe("XmlComponent", () => {
|
|||||||
assert.equal(newJson.rootKey, "w:test");
|
assert.equal(newJson.rootKey, "w:test");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("#prepForXml()", () => {
|
||||||
|
it("should skip deleted elements", () => {
|
||||||
|
const child = new TestComponent("w:test1");
|
||||||
|
child.delete();
|
||||||
|
xmlComponent.addChildElement(child);
|
||||||
|
|
||||||
|
const xml = xmlComponent.prepForXml();
|
||||||
|
assert.equal(xml["w:test"].length, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -12,6 +12,12 @@ export abstract class XmlComponent extends BaseXmlComponent {
|
|||||||
|
|
||||||
public prepForXml(): IXmlableObject {
|
public prepForXml(): IXmlableObject {
|
||||||
const children = this.root
|
const children = this.root
|
||||||
|
.filter((c) => {
|
||||||
|
if (c instanceof BaseXmlComponent) {
|
||||||
|
return !c.isDeleted;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
.map((comp) => {
|
.map((comp) => {
|
||||||
if (comp instanceof BaseXmlComponent) {
|
if (comp instanceof BaseXmlComponent) {
|
||||||
return comp.prepForXml();
|
return comp.prepForXml();
|
||||||
@ -23,4 +29,15 @@ export abstract class XmlComponent extends BaseXmlComponent {
|
|||||||
[this.rootKey]: children,
|
[this.rootKey]: children,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Unused method
|
||||||
|
public addChildElement(child: XmlComponent | string): XmlComponent {
|
||||||
|
this.root.push(child);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete(): void {
|
||||||
|
this.deleted = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user