Compare commits

...

47 Commits
3.2.0 ... 3.5.0

Author SHA1 Message Date
4d7bdc2ed9 Version bump 2018-05-08 22:19:01 +01:00
d10c707f12 Add tests for catching errors for exporter packer.pack 2018-05-08 20:40:04 +01:00
49cc8a267c Merge pull request #71 from ivanryuu/hyperlink_support
External hyperlink support
2018-05-08 01:16:28 +01:00
68cb57aea6 fix style issues 2018-05-06 23:18:00 -05:00
9d7fd55e4c add hyperlink to paragraph and doc 2018-05-06 22:24:16 -05:00
195c62f80b modify relationships to support external links 2018-05-06 22:23:35 -05:00
1fd222abea create hyperlink style 2018-05-06 22:23:04 -05:00
ac40e13e33 added support for hyperlinks 2018-05-06 22:20:56 -05:00
52e8fe576e Bump prettier version 2018-05-06 03:03:35 +01:00
b389ac6347 Add style fix command 2018-05-06 03:02:06 +01:00
8108eca2fa Update README.md 2018-04-21 00:04:14 +01:00
8c613195f3 Version bump 2018-04-10 21:55:02 +01:00
41f941728e Clean up and tests 2018-04-10 21:54:52 +01:00
ecf1542d95 Add instructions image 2018-04-10 21:42:21 +01:00
06b2bbba25 Add image demo 2018-04-10 21:36:11 +01:00
0494fdeabd Add other demo scripts as part of build 2018-04-10 21:08:40 +01:00
226206b100 Fix build 2018-04-10 21:06:02 +01:00
02f80bc616 Merge pull request #68 from citizenos/master
Updated bullet list creating
2018-04-10 21:04:27 +01:00
3189c9251a modified style script 2018-04-10 11:00:25 +03:00
ae43137906 revert demo10.js, fix paragraph tests 2018-04-10 10:25:12 +03:00
50bee30799 reverted gitignore 2018-04-09 23:21:51 +03:00
38484a3063 removed build folder 2018-04-09 23:18:55 +03:00
919327ed08 bullet points update 2018-04-05 16:50:25 +03:00
c00c5fa02d Merge pull request #67 from citizenos/master
image scaling add rounding
2018-04-03 22:51:15 +01:00
80f09ac10b modified git ignore 2018-04-02 14:37:54 +03:00
ee721ffbec image scaling add rounding 2018-04-02 12:55:43 +03:00
1dd1c65341 Version bump 2018-03-24 19:24:24 +00:00
e593327fea Merge pull request #66 from dolanmiu/feat/resizable-pictures
Feat/resizable pictures
2018-03-24 19:23:22 +00:00
64e0aeeb18 Fix demo 2018-03-24 19:11:25 +00:00
05816abc12 Add scalable image feature 2018-03-24 19:06:10 +00:00
a954c69458 Fix tests 2018-03-22 23:04:46 +00:00
a0e034bd55 Version bump 2018-03-22 22:56:05 +00:00
1a1c1f26d9 Border for tables by default 2018-03-22 22:55:33 +00:00
a102b479e6 Add new demo 2018-03-10 23:04:45 +00:00
5b5f5ea203 Merge pull request #63 from felipeochoa/stream-packer
Added generic stream packer
2018-02-25 15:40:46 +00:00
40a8e581f1 Added generic stream packer 2018-02-23 19:21:00 -05:00
1b988e7135 Remove old deployment scripts 2018-02-21 22:09:55 +00:00
9433c7aedc Update documentation links 2018-02-21 22:09:42 +00:00
479dfed987 Clean up and nojekyll 2018-02-21 21:51:17 +00:00
1866033128 Remove spec files from docs 2018-02-21 21:46:02 +00:00
844b9ab2ec Update travis for docs 2018-02-21 21:34:43 +00:00
e6713d6ce2 Ignore My Document generated file 2018-02-13 23:21:20 +00:00
069ce73c07 Add My CV Demo 2018-02-13 23:20:14 +00:00
6dda6a5513 Formatting 2018-02-13 02:10:42 +00:00
329def42ac Prettier formatting 2018-02-13 02:08:28 +00:00
c5137b5fad Add RunKit examples 2018-02-13 02:07:56 +00:00
ab05ccb846 Simplify prettier command 2018-02-10 17:56:52 +00:00
46 changed files with 1095 additions and 195 deletions

3
.gitignore vendored
View File

@ -46,3 +46,6 @@ build-tests
# Lock files
package-lock.json
# Documents
My Document.docx

5
.prettierrc.yml Normal file
View File

@ -0,0 +1,5 @@
trailingComma: all
printWidth: 140
tabWidth: 4
arrowParens: always
bracketSpacing: true

View File

@ -15,11 +15,21 @@ script:
- node ./demo/demo5.js
- node ./demo/demo6.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:
- "cat /home/travis/builds/dolanmiu/docx/npm-debug.log"
after_success:
- bash ./deploy-docs.sh
env:
global:
- ENCRYPTION_LABEL: "ad385fa3b525"
- npm run typedoc
- echo "janchi.co.uk" > docs/.nojekyll
deploy:
provider: pages
skip-cleanup: true
github-token: $GITHUB_TOKEN
keep-history: true
local-dir: docs
on:
branch: master

View File

@ -6,7 +6,7 @@
Generate .docx files with JS/TS very easily, written in TS.
</p>
-----
---
[![NPM version][npm-image]][npm-url]
[![Build Status][travis-image]][travis-url]
@ -14,6 +14,7 @@
[![Known Vulnerabilities][snky-image]][snky-url]
[![Chat on Gitter][gitter-image]][gitter-url]
[![code style: prettier][prettier-image]][prettier-url]
[![PRs Welcome][pr-image]][pr-url]
[![NPM](https://nodei.co/npm/docx.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/docx/)
@ -27,23 +28,39 @@ $ npm install --save docx
## Demo
Press `endpoint` on the `RunKit` website:
![RunKit Instructions](https://user-images.githubusercontent.com/2917613/38582539-f84311b6-3d07-11e8-90db-5885ae02c3c4.png)
* 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
$ 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
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
```js
// Used to create docx files
var docx = require('docx');
var docx = require("docx");
// Create document
var doc = new docx.Document();
@ -51,7 +68,7 @@ var doc = new docx.Document();
// Add some content in the document
var paragraph = new docx.Paragraph("Some cool text here.");
// 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);
// Used to export the file into a .docx file
@ -61,9 +78,9 @@ var exporter = new docx.LocalPacker(doc);
// res is express' Response object
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
exporter.packPdf('My First Document');
exporter.packPdf("My First Document");
// done! A file called 'My First Document.docx'
// will be in your file system if you used LocalPacker
@ -71,13 +88,14 @@ exporter.packPdf('My First Document');
```
## Examples
Check [the Wiki](https://github.com/dolanmiu/docx/wiki/Examples) for examples.
# Contributing
Read the contribution guidelines [here](https://github.com/dolanmiu/docx/wiki/Contributing-Guidelines).
-----
---
Made with 💖
@ -97,3 +115,5 @@ Huge thanks to [@felipeochoa](https://github.com/felipeochoa) for awesome contri
[gemnasium-url]: https://gemnasium.com/github.com/dolanmiu/docx
[prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg
[prettier-url]: https://github.com/prettier/prettier
[pr-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
[pr-url]: http://makeapullrequest.com

314
demo/demo10.js Normal file
View 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
View 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
View 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!");

View File

@ -29,6 +29,16 @@ doc.addParagraph(subP);
doc.addParagraph(secondSubP);
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);
exporter.pack('My Document');

View File

@ -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

Binary file not shown.

View File

@ -1,6 +1,6 @@
{
"name": "docx",
"version": "3.2.0",
"version": "3.5.0",
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
"main": "build/index.js",
"scripts": {
@ -13,8 +13,9 @@
"tsc": "rimraf ./build && tsc -p .",
"webpack": "rimraf ./build && webpack",
"demo": "npm run build && node ./demo",
"typedoc": "npm run build && typedoc --out docs/ src/ --module commonjs --target ES6 --disableOutputCheck",
"style": "prettier -l --trailing-comma all --print-width 140 --arrow-parens always \"src/**/*.ts\"",
"typedoc": "typedoc --out docs/ src/ --module commonjs --target ES6 --disableOutputCheck --excludePrivate --externalPattern \"**/*.spec.ts\"",
"style": "prettier -l \"src/**/*.ts\"",
"style.fix": "prettier \"src/**/*.ts\" --write",
"fix-types": "node types-absolute-fixer.js"
},
"files": [
@ -59,16 +60,18 @@
"devDependencies": {
"@types/chai": "^3.4.35",
"@types/mocha": "^2.2.39",
"@types/sinon": "^4.3.1",
"awesome-typescript-loader": "^3.4.1",
"chai": "^3.5.0",
"glob": "^7.1.2",
"mocha": "^3.2.0",
"mocha-webpack": "^1.0.1",
"prettier": "^1.10.2",
"prettier": "^1.12.1",
"prompt": "^1.0.0",
"replace-in-file": "^3.1.0",
"rimraf": "^2.5.2",
"shelljs": "^0.7.7",
"sinon": "^5.0.7",
"tslint": "^5.1.0",
"typedoc": "^0.9.0",
"typescript": "2.6.2",

View File

@ -1,3 +1,4 @@
export * from "./packer/local";
export * from "./packer/express";
export * from "./packer/packer";
export * from "./packer/stream";

View File

@ -1,6 +1,6 @@
import * as archiver from "archiver";
import * as express from "express";
import * as fs from "fs";
import { Writable } from "stream";
import * as xml from "xml";
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);
const xmlDocument = xml(this.formatter.format(this.file.Document), true);

View 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);
});
});
});
});

View File

@ -1,5 +1,7 @@
/* tslint:disable:typedef space-before-function-paren */
import { assert } from "chai";
import * as fs from "fs";
import { stub } from "sinon";
import { LocalPacker } from "../../export/packer/local";
import { File, Paragraph } from "../../file";
@ -29,14 +31,36 @@ describe("LocalPacker", () => {
await packer.pack("build/tests/test");
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", () => {
it("should create a standard PDF file", async function() {
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");
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);
});
});
});
});

View 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;
}
}

View File

@ -57,17 +57,17 @@ export class Document extends XmlComponent {
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 run = new PictureRun(imageData);
paragraph.addRun(run);
this.addDrawing(paragraph);
this.body.push(paragraph);
}
public createDrawing(imageData: IMediaData): void {
this.addDrawing(imageData);
return;
return run;
}
}

View File

@ -3,6 +3,8 @@ import { XmlComponent } from "file/xml-components";
import { Inline } from "./inline";
export class Drawing extends XmlComponent {
private inline: Inline;
constructor(imageData: IMediaData) {
super("w:drawing");
@ -10,6 +12,12 @@ export class Drawing extends XmlComponent {
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);
}
}

View File

@ -2,14 +2,23 @@ import { XmlComponent } from "file/xml-components";
import { ExtentAttributes } from "./extent-attributes";
export class Extent extends XmlComponent {
private attributes: ExtentAttributes;
constructor(x: number, y: number) {
super("wp:extent");
this.root.push(
new ExtentAttributes({
cx: x,
cy: y,
}),
);
this.attributes = new ExtentAttributes({
cx: x,
cy: y,
});
this.root.push(this.attributes);
}
public setXY(x: number, y: number): void {
this.attributes.set({
cx: x,
cy: y,
});
}
}

View File

@ -3,6 +3,8 @@ import { GraphicDataAttributes } from "./graphic-data-attribute";
import { Pic } from "./pic";
export class GraphicData extends XmlComponent {
private pic: Pic;
constructor(referenceId: number, x: number, y: number) {
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);
}
}

View File

@ -6,6 +6,8 @@ import { PicAttributes } from "./pic-attributes";
import { ShapeProperties } from "./shape-properties/shape-properties";
export class Pic extends XmlComponent {
private shapeProperties: ShapeProperties;
constructor(referenceId: number, x: number, y: number) {
super("pic:pic");
@ -14,8 +16,15 @@ export class Pic extends XmlComponent {
xmlns: "http://schemas.openxmlformats.org/drawingml/2006/picture",
}),
);
this.shapeProperties = new ShapeProperties(x, y);
this.root.push(new NonVisualPicProperties());
this.root.push(new BlipFill(referenceId));
this.root.push(new ShapeProperties(x, y));
}
public setXY(x: number, y: number): void {
this.shapeProperties.setXY(x, y);
}
}

View File

@ -3,14 +3,23 @@ import { XmlComponent } from "file/xml-components";
import { ExtentsAttributes } from "./extents-attributes";
export class Extents extends XmlComponent {
private attributes: ExtentsAttributes;
constructor(x: number, y: number) {
super("a:ext");
this.root.push(
new ExtentsAttributes({
cx: x,
cy: y,
}),
);
this.attributes = new ExtentsAttributes({
cx: x,
cy: y,
});
this.root.push(this.attributes);
}
public setXY(x: number, y: number): void {
this.attributes.set({
cx: x,
cy: y,
});
}
}

View File

@ -4,10 +4,18 @@ import { Extents } from "./extents/extents";
import { Offset } from "./offset/off";
export class Form extends XmlComponent {
private extents: Extents;
constructor(x: number, y: number) {
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());
}
public setXY(x: number, y: number): void {
this.extents.setXY(x, y);
}
}

View File

@ -7,6 +7,8 @@ import { PresetGeometry } from "./preset-geometry/preset-geometry";
import { ShapePropertiesAttributes } from "./shape-properties-attributes";
export class ShapeProperties extends XmlComponent {
private form: Form;
constructor(x: number, y: number) {
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 NoFill());
// this.root.push(new Outline());
}
public setXY(x: number, y: number): void {
this.form.setXY(x, y);
}
}

View 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);
}
}

View File

@ -1,24 +1 @@
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 {
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));
}
}
export * from "./graphic";

View File

@ -9,7 +9,10 @@ import { GraphicFrameProperties } from "./graphic-frame/graphic-frame-properties
import { InlineAttributes } from "./inline-attributes";
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");
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 DocProperties());
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);
}
}

View File

@ -7,7 +7,7 @@ import { FooterWrapper } from "./footer-wrapper";
import { HeaderWrapper } from "./header-wrapper";
import { Media } from "./media";
import { Numbering } from "./numbering";
import { Paragraph } from "./paragraph";
import { Hyperlink, Paragraph, PictureRun } from "./paragraph";
import { Relationships } from "./relationships";
import { Styles } from "./styles";
import { DefaultStylesFactory } from "./styles/factory";
@ -101,14 +101,26 @@ export class File {
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);
this.docRelationships.createRelationship(
mediaData.referenceId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`,
);
this.document.createDrawing(mediaData);
return this.document.createDrawing(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 {

View File

@ -1,7 +1,6 @@
import { XmlComponent } from "file/xml-components";
import { DocumentAttributes } from "../document/document-attributes";
import { Indent } from "../paragraph/formatting";
import { RunFonts } from "../paragraph/run/run-fonts";
import { AbstractNumbering } from "./abstract-numbering";
import { Num } from "./num";
@ -36,50 +35,23 @@ export class Numbering extends XmlComponent {
const abstractNumbering = this.createAbstractNumbering();
abstractNumbering
.createLevel(0, "bullet", "•", "left")
.addParagraphProperty(new Indent({ left: 720, hanging: 360 }))
.addRunProperty(new RunFonts("Symbol", "default"));
abstractNumbering.createLevel(0, "bullet", "\u25CF", "left").addParagraphProperty(new Indent({ left: 720, hanging: 360 }));
abstractNumbering
.createLevel(1, "bullet", "o", "left")
.addParagraphProperty(new Indent({ left: 1440, hanging: 360 }))
.addRunProperty(new RunFonts("Courier New", "default"));
abstractNumbering.createLevel(1, "bullet", "\u25CB", "left").addParagraphProperty(new Indent({ left: 1440, hanging: 360 }));
abstractNumbering
.createLevel(2, "bullet", "•", "left")
.addParagraphProperty(new Indent({ left: 2160, hanging: 360 }))
.addRunProperty(new RunFonts("Wingdings", "default"));
abstractNumbering.createLevel(2, "bullet", "\u25A0", "left").addParagraphProperty(new Indent({ left: 2160, hanging: 360 }));
abstractNumbering
.createLevel(3, "bullet", "•", "left")
.addParagraphProperty(new Indent({ left: 2880, hanging: 360 }))
.addRunProperty(new RunFonts("Symbol", "default"));
abstractNumbering.createLevel(3, "bullet", "\u25CF", "left").addParagraphProperty(new Indent({ left: 2880, hanging: 360 }));
abstractNumbering
.createLevel(4, "bullet", "o", "left")
.addParagraphProperty(new Indent({ left: 3600, hanging: 360 }))
.addRunProperty(new RunFonts("Courier New", "default"));
abstractNumbering.createLevel(4, "bullet", "\u25CB", "left").addParagraphProperty(new Indent({ left: 3600, hanging: 360 }));
abstractNumbering
.createLevel(5, "bullet", "•", "left")
.addParagraphProperty(new Indent({ left: 4320, hanging: 360 }))
.addRunProperty(new RunFonts("Wingdings", "default"));
abstractNumbering.createLevel(5, "bullet", "\u25A0", "left").addParagraphProperty(new Indent({ left: 4320, hanging: 360 }));
abstractNumbering
.createLevel(6, "bullet", "•", "left")
.addParagraphProperty(new Indent({ left: 5040, hanging: 360 }))
.addRunProperty(new RunFonts("Symbol", "default"));
abstractNumbering.createLevel(6, "bullet", "\u25CF", "left").addParagraphProperty(new Indent({ left: 5040, hanging: 360 }));
abstractNumbering
.createLevel(7, "bullet", "o", "left")
.addParagraphProperty(new Indent({ left: 5760, hanging: 360 }))
.addRunProperty(new RunFonts("Courier New", "default"));
abstractNumbering.createLevel(7, "bullet", "\u25CB", "left").addParagraphProperty(new Indent({ left: 5760, hanging: 360 }));
abstractNumbering
.createLevel(8, "bullet", "•", "left")
.addParagraphProperty(new Indent({ left: 6480, hanging: 360 }))
.addRunProperty(new RunFonts("Wingdings", "default"));
abstractNumbering.createLevel(8, "bullet", "\u25A0", "left").addParagraphProperty(new Indent({ left: 6480, hanging: 360 }));
this.createConcreteNumbering(abstractNumbering);
}

View File

@ -2,3 +2,4 @@ export * from "./formatting";
export * from "./paragraph";
export * from "./properties";
export * from "./run";
export * from "./links";

View 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",
};
}

View 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);
});
});
});

View 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"));
}
}

View File

@ -0,0 +1 @@
export * from "./hyperlink";

View File

@ -162,7 +162,7 @@ describe("Paragraph", () => {
});
describe("#bullet()", () => {
it("should add list paragraph style to JSON", () => {
it("should default to 0 indent level if no bullet was specified", () => {
paragraph.bullet();
const tree = new Formatter().format(paragraph);
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", () => {
paragraph.bullet();
paragraph.bullet(1);
const tree = new Formatter().format(paragraph);
expect(tree)
.to.have.property("w:p")
@ -190,7 +206,7 @@ describe("Paragraph", () => {
.which.is.an("array")
.which.has.length.at.least(2);
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 } }] }],
});
});
});

View File

@ -13,6 +13,7 @@ import { ISpacingProperties, Spacing } from "./formatting/spacing";
import { Style } from "./formatting/style";
import { CenterTabStop, LeftTabStop, MaxRightTabStop, RightTabStop } from "./formatting/tab-stop";
import { NumberProperties } from "./formatting/unordered-list";
import { Hyperlink } from "./links";
import { ParagraphProperties } from "./properties";
export class Paragraph extends XmlComponent {
@ -32,6 +33,11 @@ export class Paragraph extends XmlComponent {
return this;
}
public addHyperLink(hyperlink: Hyperlink): Paragraph {
this.root.push(hyperlink);
return this;
}
public createTextRun(text: string): TextRun {
const run = new TextRun(text);
this.addRun(run);
@ -69,6 +75,11 @@ export class Paragraph extends XmlComponent {
return this;
}
public heading6(): Paragraph {
this.properties.push(new Style("Heading6"));
return this;
}
public title(): Paragraph {
this.properties.push(new Style("Title"));
return this;
@ -124,9 +135,9 @@ export class Paragraph extends XmlComponent {
return this;
}
public bullet(): Paragraph {
public bullet(indentLevel: number = 0): Paragraph {
this.properties.push(new Style("ListParagraph"));
this.properties.push(new NumberProperties(1, 0));
this.properties.push(new NumberProperties(1, indentLevel));
return this;
}

View File

@ -3,6 +3,8 @@ import { IMediaData } from "../../media/data";
import { Run } from "../run";
export class PictureRun extends Run {
private drawing: Drawing;
constructor(imageData: IMediaData) {
super();
@ -10,6 +12,20 @@ export class PictureRun extends Run {
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);
}
}

View File

@ -4,6 +4,7 @@ export interface IRelationshipAttributesProperties {
id: string;
type: string;
target: string;
targetMode?: string;
}
export class RelationshipAttributes extends XmlAttributeComponent<IRelationshipAttributesProperties> {
@ -11,5 +12,6 @@ export class RelationshipAttributes extends XmlAttributeComponent<IRelationshipA
id: "Id",
type: "Type",
target: "Target",
targetMode: "TargetMode",
};
}

View File

@ -13,10 +13,13 @@ export type RelationshipType =
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
| "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 {
constructor(id: string, type: RelationshipType, target: string) {
constructor(id: string, type: RelationshipType, target: string, targetMode?: TargetModeType) {
super("Relationship");
this.root.push(
@ -24,6 +27,7 @@ export class Relationship extends XmlComponent {
id,
type,
target,
targetMode,
}),
);
}

View File

@ -1,6 +1,6 @@
import { XmlComponent } from "file/xml-components";
import { RelationshipsAttributes } from "./attributes";
import { Relationship, RelationshipType } from "./relationship/relationship";
import { Relationship, RelationshipType, TargetModeType } from "./relationship/relationship";
export class Relationships extends XmlComponent {
constructor() {
@ -16,8 +16,8 @@ export class Relationships extends XmlComponent {
this.root.push(relationship);
}
public createRelationship(id: number, type: RelationshipType, target: string): Relationship {
const relationship = new Relationship(`rId${id}`, type, target);
public createRelationship(id: number, type: RelationshipType, target: string, targetMode?: TargetModeType): Relationship {
const relationship = new Relationship(`rId${id}`, type, target, targetMode);
this.addRelationship(relationship);
return relationship;

View File

@ -9,6 +9,7 @@ import {
Heading4Style,
Heading5Style,
Heading6Style,
HyperlinkStyle,
ListParagraph,
TitleStyle,
} from "./style";
@ -54,6 +55,8 @@ export class DefaultStylesFactory {
// listParagraph.addParagraphProperty();
styles.push(listParagraph);
const hyperLinkStyle = new HyperlinkStyle();
styles.push(hyperLinkStyle);
return styles;
}
}

View File

@ -3,7 +3,7 @@ import * as paragraph from "../../paragraph";
import * as formatting from "../../paragraph/run/formatting";
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 {
type?: string;
@ -249,3 +249,43 @@ export class ListParagraph extends ParagraphStyle {
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");
}
}

View File

@ -16,6 +16,11 @@ export class TableProperties extends XmlComponent {
this.root.push(new TableLayout("fixed"));
return this;
}
public setBorder(): TableProperties {
this.root.push(new TableBorders());
return this;
}
}
interface ITableWidth {
@ -46,3 +51,38 @@ class TableLayout extends XmlComponent {
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",
};
}

View File

@ -5,6 +5,83 @@ import { Formatter } from "../../export/formatter";
import { Paragraph } from "../paragraph";
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("#constructor", () => {
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": [] }] }] };
expect(tree).to.deep.equal({
"w:tbl": [
{ "w:tblPr": [] },
{ "w:tblPr": [DEFAULT_TABLE_PROPERTIES] },
{
"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({
"w:tbl": [
{ "w:tblPr": [] },
{ "w:tblPr": [DEFAULT_TABLE_PROPERTIES] },
{
"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({
"w:tbl": [
{ "w:tblPr": [] },
{ "w:tblPr": [DEFAULT_TABLE_PROPERTIES] },
{
"w:tblGrid": [{ "w:gridCol": [{ _attr: { "w:w": 1 } }] }, { "w:gridCol": [{ _attr: { "w:w": 1 } }] }],
},
@ -104,7 +181,7 @@ describe("Table", () => {
.which.is.an("array")
.with.has.length.at.least(1);
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")
.with.has.length.at.least(1);
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" } }] }],
});
});
});

View File

@ -12,6 +12,7 @@ export class Table extends XmlComponent {
super("w:tbl");
this.properties = new TableProperties();
this.root.push(this.properties);
this.properties.setBorder();
const gridCols: number[] = [];
for (let i = 0; i < cols; i++) {

View File

@ -23,4 +23,8 @@ export abstract class XmlAttributeComponent<T> extends BaseXmlComponent {
});
return { _attr: attrs };
}
public set(properties: T): void {
this.root = properties;
}
}