Compare commits

..

175 Commits

Author SHA1 Message Date
9e8d67c4a9 Version bump 2019-06-28 01:59:18 +01:00
cbbc4a80e7 FIx up API for column count 2019-06-28 01:57:43 +01:00
1f51fd7a31 Remove usage of Utility 2019-06-27 01:35:58 +01:00
120d97bc64 Remove Utility 2019-06-27 01:10:39 +01:00
a531713214 Remove usage of Utility class 2019-06-26 22:12:18 +01:00
564a055316 Remove jsonify method 2019-06-26 02:11:43 +01:00
6c29c4fb1f Merge pull request #343 from markcharyk/no-pretty-xml
Make prettified XML a Packer-wide configurable option
2019-06-26 00:44:33 +01:00
552580bc47 Merge pull request #340 from filippomuscolino/feat/row-height
Add table row height
2019-06-25 20:18:17 +01:00
0998d43b8f Merge pull request #341 from filippomuscolino/fix/missing-exports
Fix: add missing exports
2019-06-25 15:38:13 +01:00
b08354494c Improve tests 2019-06-25 14:48:45 +02:00
f54b9a197d Fix: add missing exports 2019-06-25 14:35:35 +02:00
2d4412ce51 Add table row height 2019-06-25 12:02:53 +02:00
72c32378c5 Make prettified XML a Packer-wide configurable option
The xml() function was being passed a flag for pretty printing the XML in word/document.xml. This was causing some parsers (not Word itself) to break when they didn't expect whitepsace. It was also causing some files to be prettified and others to be ugly. This ensures consistent prettification.
2019-06-21 13:53:01 -07:00
0c79c0ac83 Merge pull request #337 from zahrafali/multiple-columns
Multiple columns in a section
2019-06-21 15:36:33 +01:00
1dfc27ba08 Multiple columns in a section 2019-06-21 01:22:27 +05:30
58dc6fe389 Format demo 2019-06-12 01:12:32 +01:00
34c3285426 Version bump 2019-06-12 01:07:53 +01:00
a5afce458d Merge pull request #262 from aravindballa/master
Feature: add line numbers to section
2019-06-05 17:38:49 +01:00
2f687125d1 fix lint issue in section-properties.ts 2019-06-05 13:01:24 +05:30
5c1a731314 Version bump 2019-05-16 22:15:50 +01:00
622331ad24 Merge pull request #324 from Scarface2013/master
Fix merging of 3+ cells vertically
2019-05-16 21:06:20 +01:00
3a593a53d3 Fix tests 2019-05-16 11:15:20 -04:00
31a9111667 Fix merging of 3+ cells vertically 2019-05-15 18:14:39 -04:00
373c890354 Add docsify serve 2019-04-30 00:30:18 +01:00
723d76d06f Merge pull request #314 from efx/fix-doc-links
fix broken documentation links
2019-04-29 12:08:27 +01:00
a24d745d75 fix broken documentation links 2019-04-25 09:49:44 -04:00
90891cfafd Merge pull request #310 from nickgeorgiou/feat/margin-spelling-fix
Fix spelling of "margin"
2019-04-18 17:10:00 +01:00
29f890918c Fix spelling of "margin" 2019-04-18 13:55:18 +10:00
b0f8f8ddbd Merge pull request #308 from filippomuscolino/master
Update documentation
2019-04-12 14:43:32 +01:00
9c89c1ab59 Update docs 2019-04-12 13:46:05 +02:00
7a9cb92955 Merge pull request #307 from brucehappy/features/xml_optimization
Optimize XML output
2019-04-11 22:50:10 +01:00
77edf8862b Added constant EMPTY_OBJECT (an empty sealed object) that is used to indicate to the xml library that an empty XML element should be generated, and use it in the JSON hardcoded into the tests. 2019-04-10 13:47:38 -04:00
bb1604cd8f Turn back on no-null-keyword. Use empty object instead of null to signal to the xml library that an empty element should be produced. Update the related tests.
Related to #306
2019-04-10 01:28:37 -04:00
816cb54b14 Optimize XML output by properly constructing objects to send to the xml library so that it can produce proper empty elements.
Rework the way attributes are stored in ImportedXmlComponent to match elsewhere (required allowing for a null xmlKeys in the XmlAttributeComponent interface).
Rework the way paragraphs get added to the end of table cells if needed.
The goal in both reworks is to not mess around with the objects output from `prepForXml` if we can avoid it.
Made the output of RunProperties, ParagraphProperties, TableCellProperties, TableRowProperties, and TableProperties all optional based on whether they contain any attributes or children.  Changed code in PageBorders, TableCellMargin, and TableCellBorders that implemented this same thing by overriding `prepForXml` so that it uses the new XmlComponent subclass instead.
Removed commented out code that attempted to fix-up XML output and make proper empty elements.
Fixed all affected tests.
Turn off `no-null-keyword` in the linter as we need to use null to signal to the `xml` library to create an empty element with no attributes (`undefined` will not work in its place).

Fixes #306
2019-04-09 05:27:18 -04:00
920bd3c175 Merge pull request #304 from brucehappy/issues/issue_303
Fix table cell margin type XML attribute
2019-04-08 20:30:54 +01:00
a2a01edc24 Change table cell margin type attribute form w:sz to w:type as per http://officeopenxml.com/WPtableCellMargins.php
Fixes #303
2019-04-08 13:50:40 -04:00
193d0c4239 Merge pull request #299 from filippomuscolino/fix-duplicated-section
Fix: duplicated generation of last section properties
2019-04-05 01:07:10 +01:00
d8cc11c5ab Merge pull request #300 from efx/fix-typo
fix minor typos
2019-04-05 01:03:39 +01:00
b874051f32 fix minor typos 2019-04-04 15:35:41 -04:00
272e2496f4 Fix duplicated generation of last section properties 2019-04-04 17:43:54 +02:00
43c199c725 Version bump 2019-03-30 15:45:12 +00:00
0e975b3d66 Merge pull request #292 from dolanmiu/feature/table-background
Add shading (background color) for cell
2019-03-21 01:33:11 +00:00
c6ab47e838 Add shading 2019-03-21 01:06:07 +00:00
48c17d51bb Merge pull request #288 from dolanmiu/feat/table
Add margains, widths and floats to tables
2019-03-19 00:19:55 +00:00
df2315ae4a Disable e2e for demo 32 temporarily 2019-03-19 00:07:11 +00:00
e67fd9cb2b Add tests and margain 2019-03-18 23:50:21 +00:00
639842332f Change contribution guidelines 2019-03-13 23:13:10 +00:00
2cb7d44a77 Work on new table API 2019-03-13 02:29:11 +00:00
6cd62418a5 Update README.md 2019-03-12 20:26:39 +00:00
f3ba11b21c Improving table api 2019-03-08 01:09:21 +00:00
51b1e08a35 Merge pull request #284 from askoufis/numbering-docs-typo
Fixes typo in numbering documentation
2019-03-07 17:08:33 +00:00
b0d0041ff9 Rewording and remove # 2019-03-07 15:36:06 +11:00
55c5dd818a More typos 2019-03-07 15:33:32 +11:00
3bebe0ad59 Found another typo 2019-03-07 15:32:16 +11:00
c5c67fd92a Fixes typo in numbering documentation 2019-03-07 15:28:30 +11:00
1486d3de56 Improve documentation 2019-03-07 01:01:01 +00:00
6d8eea40be Merge branch 'master' of github.com:dolanmiu/docx 2019-03-06 22:53:23 +00:00
0922d49cd3 Merge pull request #282 from filippomuscolino/fix/table-width
Fix: table width in percentage should include '%'
2019-03-06 13:52:49 +00:00
f76471f553 Fix test 2019-03-06 13:33:50 +01:00
973177676e fix: table width in percentage should include '%' 2019-03-06 12:21:15 +01:00
5aa878b901 Fix invalid link 2019-03-06 02:12:41 +00:00
b99d1fc129 correctly use include 2019-03-06 00:18:00 +00:00
1590f9ac70 Merge pull request #274 from dolanmiu/feat/vertical-table-merge
Add table column and vertical merging
2019-03-05 23:53:12 +00:00
6c772b956c Add examples to table documentation 2019-03-05 23:15:50 +00:00
9d09ff3518 Update documentation 2019-03-05 21:57:31 +00:00
d9ad69b7cd Merge pull request #278 from filippomuscolino/feat/style-improvements
Add bold and italics to character style + customize hyperlink
2019-03-05 20:59:21 +00:00
e23d230acb Merge pull request #280 from filippomuscolino/feat/justify-line-break
Add setting for not justify lines ending in soft line break
2019-03-05 20:55:21 +00:00
5ca872cb07 Merge pull request #279 from filippomuscolino/settings-fix
Fix: settings are ignored by Word without relationship and content type
2019-03-05 17:44:21 +00:00
b179facca2 Add setting for not justify lines ending in soft line break 2019-03-05 17:17:06 +01:00
db60270e1f fix: relationship and content type for settings 2019-03-05 15:40:00 +01:00
3e2130bc80 Customize text run of an hyperlink 2019-03-05 15:15:58 +01:00
1b336785b4 Add bold and italics to character style 2019-03-05 15:15:45 +01:00
6eed0fe0f5 Update contribution-guidelines.md 2019-03-05 13:32:09 +00:00
2f8f69b0c4 Update README.md 2019-03-05 13:29:36 +00:00
4580a65a84 Merge pull request #277 from filippomuscolino/feat/style-outline
Select styles that will go into TOC
2019-03-05 13:22:22 +00:00
661c3e21e4 Merge pull request #275 from filippomuscolino/feat/table-pagination
Table pagination: add cantSplit and tblHeader row properties
2019-03-05 11:24:08 +00:00
32646f8806 Add outline level to paragraph style 2019-03-05 12:19:36 +01:00
231428852e Merge pull request #276 from filippomuscolino/feat/cell-properties
Cell borders on Google Docs + set cell width
2019-03-05 11:19:29 +00:00
46ddf77342 Get cell properties 2019-03-05 11:38:21 +01:00
441afe8c97 Add tests 2019-03-05 11:34:43 +01:00
ca9ce01f56 Customize left and right cell borders (for Google Docs) 2019-03-05 11:09:47 +01:00
2b9ce0febd Add tests 2019-03-05 11:04:54 +01:00
18760387db Table pagination: add cantSplit and tblHeader row properties 2019-03-05 10:13:53 +01:00
28dabb06cb Write getColumn test 2019-03-05 01:58:56 +00:00
50fc9b6274 Test index out of bounds 2019-03-05 01:45:04 +00:00
367518d504 Add tests for table column 2019-03-05 01:39:51 +00:00
efd89f24e6 Add table column and vertical merging 2019-03-04 22:50:04 +00:00
4fd2b6f1d3 Merge branch 'feat/table-merge-fix' 2019-02-26 22:32:21 +00:00
5ae541a40d Fix descriptions of demos 2019-02-26 22:20:20 +00:00
c9fb9a827d Version bump 2019-02-26 21:54:33 +00:00
918faf59c3 Merge pull request #273 from dolanmiu/feat/table-merge-fix
Add higher width for GridCol to fix merging cells
2019-02-26 21:53:14 +00:00
3a9fa49fbb Fix test 2019-02-26 21:46:19 +00:00
218a08d793 Fix tests 2019-02-26 21:45:55 +00:00
416a239708 Add higher width for grid col to fix merging 2019-02-26 21:38:54 +00:00
d1bdbd397a Merge pull request #265 from joefitter/bugfix/conditional-coverage
dont run coverage if publishing
2019-02-09 17:43:43 +00:00
a6077b8f16 dont run coverage if publishing 2019-02-08 12:06:50 +00:00
8f133ff93a add example for line numbers 2019-02-06 13:26:18 +05:30
1f12e159ef add line numbers to section 2019-02-04 18:49:12 +05:30
83a7f4664d Update tslint.json 2019-01-28 13:04:51 +00:00
728aefc4a7 Merge pull request #256 from dolanmiu/feat/webpack-ts
Use a typescript webpack config
2019-01-24 01:16:08 +01:00
40730548bb Add webpack type definitions 2019-01-23 20:09:32 +00:00
d6c2c96757 Merge pull request #248 from filippomuscolino/master
Add OutlineLevel Paragraph property
2019-01-17 01:21:55 +01:00
b22f565dd0 Use a typescript webpack config 2019-01-16 13:34:32 +00:00
41eeac1b05 Update README.md 2019-01-16 13:26:26 +00:00
18a5f22f4c Version bump 2019-01-16 12:05:21 +01:00
2a1161d857 Add documentation 2019-01-16 12:05:21 +01:00
4b6d3c3e3c Write tests 2019-01-16 12:05:21 +01:00
14a1d62148 Add Number of pages element 2019-01-16 12:05:21 +01:00
1f98d9461b Merge pull request #250 from dolanmiu/feat/number-of-pages
Feat/number of pages
2019-01-16 01:18:11 +01:00
788205b14d Merge pull request #247 from dolanmiu/feat/number-of-pages
Add Total Number of pages element
2019-01-15 22:47:51 +01:00
bf8dfe6604 Fix for prettier 2019-01-15 16:13:20 +01:00
b0ee0305fb Add OutlineLevel Paragraph property 2019-01-15 15:39:48 +01:00
383c6d769f Merge pull request #246 from dolanmiu/feat/e2e-2
Add new e2e command
2019-01-15 02:17:21 +01:00
9f38a4f48a Test 32 instead of 30 2019-01-15 01:04:53 +00:00
475a132ce0 Fix e2e test 2019-01-15 00:50:38 +00:00
4111413074 Add relative pathing 2019-01-15 00:43:48 +00:00
50209bb435 Add Error for e2e test 2019-01-15 00:34:40 +00:00
b37d2c141d Add new e2e command 2019-01-15 00:23:01 +00:00
d19cdcae0c Rename methods 2019-01-11 01:45:57 +00:00
3f652764e9 Merge pull request #234 from dolanmiu/feat/improve-docs
Make ImportDotx easier to read
2019-01-11 01:15:42 +00:00
7b5cf690e2 Merge pull request #242 from dolanmiu/feat/more-tests2
Add more tests to paragraph
2019-01-11 00:29:45 +00:00
d674523139 Fix tests - No longer uses dummy variable 2019-01-11 00:26:16 +00:00
db7f27a88c Add more tests to paragraph 2019-01-11 00:16:25 +00:00
677300e34f Merge pull request #237 from dolanmiu/feat/image-wrap
Merge 4.6.0 into master
2019-01-10 23:53:00 +00:00
9271b2d11a Merge pull request #235 from dolanmiu/feat/floating-images
Feat/floating images
2019-01-09 21:33:52 +00:00
942c6d028c Remove unessesary null check
Trust the compiler and code
2019-01-09 21:19:57 +00:00
f717126145 Remove image-size dependency 2019-01-09 02:08:47 +00:00
abd5ace85c Merge branch 'master' of https://github.com/dolanmiu/docx into feat/floating-images
# Conflicts:
#	src/file/drawing/drawing.ts
2019-01-09 02:04:06 +00:00
612c024b1f Use string style factory 2019-01-07 21:43:04 +00:00
12ad545fe8 Write ImportDotx more functionally 2019-01-07 21:41:39 +00:00
6d0f6a61d7 Merge pull request #231 from Vivekananda-Athukuri/master
Export Num class
2019-01-04 12:06:14 +00:00
e3bcad6d3c Fix missing media in Document Template 2019-01-04 00:11:50 +00:00
f7c372a85c Merge branch 'master' into feat/improve-docs 2019-01-03 20:56:05 +00:00
bb277a4a76 Format demo 2019-01-03 20:47:00 +00:00
52b78a583e Merge pull request #202 from dolanmiu/feat/correct-mine-type
Remove async await for Compiler.compile
2019-01-03 10:45:56 +00:00
20ab36191b Merge pull request #228 from dolanmiu/feat/improve-docs
Change table methods and document it
2019-01-03 02:43:16 +00:00
dbfb80e660 Add more tests 2019-01-03 02:36:56 +00:00
421f4471de Merge branch 'master' into feat/improve-docs
# Conflicts:
#	src/file/footer-wrapper.spec.ts
2019-01-03 02:12:05 +00:00
04ea2b2dc9 Add footer tests and improve table documentation 2019-01-03 02:11:04 +00:00
93a2404343 Merge pull request #229 from microbit-matt-hillsdon/get-started-packer-fix
Fix getting started docs for v4 packer.
2019-01-02 16:15:41 +00:00
149cda9a3b Fix getting started docs for v4 packer. 2019-01-02 15:56:13 +00:00
b9465b2a20 Add more changes to table API and documentation 2018-12-31 01:55:15 +00:00
ab07f2ecbe Change table methods and document it 2018-12-29 01:57:20 +00:00
c70c147afe Merge pull request #217 from dolanmiu/feat/fix-header-image-bug
Feat/fix header image bug
2018-12-28 15:19:44 +00:00
8db52212ab Remove id from media 2018-12-24 16:50:53 +00:00
d021a3995e Sending media data down image 2018-12-21 01:39:20 +00:00
1ea347ed21 Clean up 2018-12-18 23:37:21 +00:00
0725e54271 Add tests 2018-12-16 01:56:42 +00:00
3b6aca2451 Merge pull request #220 from efx/topic-docs
add documentation on positioning units
2018-12-12 00:17:55 +00:00
249e44532f add documentation on positioning units
See further discussions in #50, #191.
2018-12-11 16:49:18 -05:00
d3af3663ec Merge branch 'release/4.4.1' 2018-12-10 19:33:00 +00:00
bf18154fcf Fix style 2018-12-07 15:34:27 +00:00
5efc9e383b Update index.html 2018-12-06 14:30:24 +00:00
6505119d3b Merge pull request #205 from dolanmiu/refactor/style-refactor
Refactor in Style
2018-12-05 21:09:30 +00:00
600f97de29 Update README.md 2018-12-05 20:11:46 +00:00
a1c21d2143 Add space 2018-12-05 19:47:56 +00:00
39066fb5f2 Add lifecycles to add image 2018-12-05 00:05:11 +00:00
8fd99052fb Merge pull request #207 from dolanmiu/feat/rename-italic
Breaking change. Make all italic to italics.
2018-11-18 15:47:35 +00:00
4e671eb512 Breaking change. Make all italic to italics. 2018-11-18 15:22:23 +00:00
2677c4a4ec Merge pull request #206 from dolanmiu/feat/refactor-setwidth
Breaking Change. Swap arguments around and make default WidthType
2018-11-15 03:08:00 +00:00
e19890e27a Breaking Change. Swap arguments around and make default WidthType 2018-11-15 03:00:26 +00:00
28556f277b Update contribution-guidelines.md 2018-11-13 14:38:26 +00:00
50e08f198c Linter ajusts 2018-11-13 11:04:29 -02:00
7639b60b15 Refactor to separate classes in their specific files an tests improuvements for styles 2018-11-13 11:04:03 -02:00
765a9686d8 Remove async await for Compiler.compile 2018-11-12 13:17:53 +00:00
ff443aa7c4 Merge pull request #201 from dolanmiu/feat/correct-mine-type
Add correct docx mime type
2018-11-12 13:12:54 +00:00
28fc328cb3 Add more packer tests 2018-11-12 13:03:17 +00:00
083f121683 Add correct docx mime type 2018-11-12 12:32:07 +00:00
2f57d3be84 Merge pull request #197 from dolanmiu/bugfix/correct-section-page-num-type
Bugfix - Correct default section options
2018-11-11 23:05:20 +00:00
7b47dadede Fix Prettier styling 2018-11-11 22:54:35 +00:00
8ad5485347 Improuvments in headers and footers docs 2018-11-09 10:37:36 -02:00
8df78e45d9 Update in examples documentation. 2018-11-09 09:26:27 -02:00
39cef5e61d Demo 16 was updated to show how page number types format and start work. 2018-11-09 09:14:38 -02:00
5552f9d834 The page number type attribute of the sections was always been created, leading Word to always reset page numbers to zero in new sections.
The page number type DECIMAL is already the default in Word, there is no need to force this to be the default option in the default section, like it was.
2018-11-09 09:13:27 -02:00
d13795696d Update index.ts 2018-11-09 12:26:23 +05:30
188 changed files with 6491 additions and 2748 deletions

View File

@ -10,6 +10,7 @@ script:
- npm run style
- npm run build
- npm run ts-node -- ./demo/demo1.ts
- npm run e2e "My Document.docx"
- npm run ts-node -- ./demo/demo2.ts
- npm run ts-node -- ./demo/demo3.ts
- npm run ts-node -- ./demo/demo4.ts
@ -19,6 +20,7 @@ script:
- npm run ts-node -- ./demo/demo8.ts
- npm run ts-node -- ./demo/demo9.ts
- npm run ts-node -- ./demo/demo10.ts
- npm run e2e "My Document.docx"
- npm run ts-node -- ./demo/demo11.ts
- npm run ts-node -- ./demo/demo12.ts
- npm run ts-node -- ./demo/demo13.ts
@ -41,6 +43,7 @@ script:
- npm run ts-node -- ./demo/demo30.ts
- npm run ts-node -- ./demo/demo31.ts
- npm run ts-node -- ./demo/demo32.ts
# - npm run e2e "My Document.docx" // Need to fix
- npm run ts-node -- ./demo/demo33.ts
- npm run ts-node -- ./demo/demo34.ts
after_failure:

View File

@ -25,7 +25,7 @@
## Browser
Here are examples of `docx` being used with basic `HTML/JS` in a browser environment.
Here are examples of `docx` being used with basic `HTML/JS` in a browser environment:
* https://codepen.io/anon/pen/dqoVgQ
* https://jsfiddle.net/3xhezb5w/2
@ -64,8 +64,17 @@ Check the `examples` section in the [documentation](https://docx.js.org/#/exampl
Read the contribution guidelines [here](https://docx.js.org/#/contribution-guidelines).
# Used by
[<img src="https://i.imgur.com/zy5qWmI.png" alt="drawing" height="200"/>](https://hfour.com/)
[<img src="https://i.imgur.com/OYP5tgS.png" alt="drawing" height="200"/>](https://fuzzproductions.com/)
[<img src="https://i.imgur.com/zUDMfZ3.png" alt="drawing" height="200"/>](https://www.mettzer.com/)
---
[![patreon][patreon-image]][patreon-url]
[![browserstack][browserstack-image]][browserstack-url]
Made with 💖
[npm-image]: https://badge.fury.io/js/docx.svg
@ -84,3 +93,7 @@ Made with 💖
[pr-url]: http://makeapullrequest.com
[codecov-image]: https://codecov.io/gh/dolanmiu/docx/branch/master/graph/badge.svg
[codecov-url]: https://codecov.io/gh/dolanmiu/docx
[patreon-image]: https://user-images.githubusercontent.com/2917613/51251459-4e880480-1991-11e9-92bf-38b96675a9e2.png
[patreon-url]: https://www.patreon.com/dolanmiu
[browserstack-image]: https://user-images.githubusercontent.com/2917613/54233552-128e9d00-4505-11e9-88fb-025a4e04007c.png
[browserstack-url]: https://www.browserstack.com

View File

@ -1,4 +1,4 @@
// Add images to header and footer
// Generate a CV
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph, TextRun } from "../build";
@ -233,7 +233,7 @@ class DocumentCreator {
public createRoleText(roleText: string): Paragraph {
const paragraph = new Paragraph();
const role = new TextRun(roleText).italic();
const role = new TextRun(roleText).italics();
paragraph.addRun(role);

View File

@ -84,8 +84,7 @@ doc.Styles.createParagraphStyle("ListParagraph", "List Paragraph")
.basedOn("Normal");
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"));
doc
.createParagraph("HEADING")
doc.createParagraph("HEADING")
.heading1()
.center();
@ -107,12 +106,15 @@ doc.createParagraph("Sir,").style("normalPara");
doc.createParagraph("BRIEF DESCRIPTION").style("normalPara");
const table = new Table(4, 4);
const table = new Table({
rows: 4,
columns: 4,
});
table
.getRow(0)
.getCell(0)
.addContent(new Paragraph("Pole No."));
// table.Properties.width = 10000;
.addParagraph(new Paragraph("Pole No."));
doc.addTable(table);
const arrboth = [
@ -127,10 +129,7 @@ const arrboth = [
];
arrboth.forEach((item) => {
const para = doc.createParagraph();
para.addImage(doc.createImage(fs.readFileSync(item.image)));
// para.Properties.width = 60;
// para.Properties.height = 90;
doc.createImage(fs.readFileSync(item.image));
doc.createParagraph(item.comment).style("normalPara2");
});

View File

@ -1,7 +1,7 @@
// Multiple sections and headers
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, PageNumberFormat, PageOrientation, Paragraph } from "../build";
import { Document, Packer, PageNumberFormat, PageOrientation, Paragraph, TextRun } from "../build";
const doc = new Document();
@ -41,6 +41,42 @@ doc.addSection({
doc.createParagraph("hello in landscape");
const header2 = doc.createHeader();
const pageNumber = new TextRun("Page number: ").pageNumber();
header2.createParagraph().addRun(pageNumber);
doc.addSection({
headers: {
default: header2,
},
orientation: PageOrientation.PORTRAIT,
});
doc.createParagraph("Page number in the header must be 2, because it continues from the previous section.");
doc.addSection({
headers: {
default: header2,
},
pageNumberFormatType: PageNumberFormat.UPPER_ROMAN,
orientation: PageOrientation.PORTRAIT,
});
doc.createParagraph(
"Page number in the header must be III, because it continues from the previous section, but is defined as upper roman.",
);
doc.addSection({
headers: {
default: header2,
},
pageNumberFormatType: PageNumberFormat.DECIMAL,
pageNumberStart: 25,
orientation: PageOrientation.PORTRAIT,
});
doc.createParagraph("Page number in the header must be 25, because it is defined to start at 25 and to be decimal in this section.");
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {

View File

@ -5,11 +5,14 @@ import { BorderStyle, Document, Packer, Paragraph } from "../build";
const doc = new Document();
const table = doc.createTable(4, 4);
const table = doc.createTable({
rows: 4,
columns: 4,
});
table
.getCell(2, 2)
.addContent(new Paragraph("Hello"))
.CellProperties.Borders.addTopBorder(BorderStyle.DASH_DOT_STROKED, 3, "red")
.addParagraph(new Paragraph("Hello"))
.Borders.addTopBorder(BorderStyle.DASH_DOT_STROKED, 3, "red")
.addBottomBorder(BorderStyle.DOUBLE, 3, "blue")
.addStartBorder(BorderStyle.DOT_DOT_DASH, 3, "green")
.addEndBorder(BorderStyle.DOT_DOT_DASH, 3, "#ff8000");

View File

@ -16,7 +16,7 @@ paragraph2.addRun(textRun2);
doc.addParagraph(paragraph2);
const paragraph3 = new Paragraph().bidirectional();
const textRun3 = new TextRun("שלום עולם").italic().rightToLeft();
const textRun3 = new TextRun("שלום עולם").italics().rightToLeft();
paragraph3.addRun(textRun3);
doc.addParagraph(paragraph3);

View File

@ -5,11 +5,14 @@ import { Document, Media, Packer, Paragraph } from "../build";
const doc = new Document();
const table = doc.createTable(4, 4);
table.getCell(2, 2).addContent(new Paragraph("Hello"));
const table = doc.createTable({
rows: 4,
columns: 4,
});
table.getCell(2, 2).addParagraph(new Paragraph("Hello"));
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
table.getCell(1, 1).addContent(image.Paragraph);
table.getCell(1, 1).addParagraph(image.Paragraph);
const packer = new Packer();

View File

@ -1,3 +1,5 @@
// Custom styles using JavaScript configuration
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer } from "../build";

View File

@ -1,4 +1,4 @@
// Creates two paragraphs, one with a border and one without
// Table of contents
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { File, Packer, Paragraph, StyleLevel, TableOfContents } from "../build";

View File

@ -1,3 +1,5 @@
// Numbered lists
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Indent, Numbering, Packer, Paragraph } from "../build";
@ -15,10 +17,22 @@ const item2 = new Paragraph("line with contextual spacing");
const item3 = new Paragraph("line without contextual spacing");
const item4 = new Paragraph("line without contextual spacing");
item1.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(true);
item2.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(true);
item3.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(false);
item4.setNumbering(concrete, 0).spacing({before: 200}).contextualSpacing(false);
item1
.setNumbering(concrete, 0)
.spacing({ before: 200 })
.contextualSpacing(true);
item2
.setNumbering(concrete, 0)
.spacing({ before: 200 })
.contextualSpacing(true);
item3
.setNumbering(concrete, 0)
.spacing({ before: 200 })
.contextualSpacing(false);
item4
.setNumbering(concrete, 0)
.spacing({ before: 200 })
.contextualSpacing(false);
doc.addParagraph(item1);
doc.addParagraph(item2);

View File

@ -1,3 +1,5 @@
// Example on how to use a template document
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, ImportDotx, Packer, Paragraph } from "../build";

View File

@ -5,15 +5,18 @@ import { Document, Packer, Paragraph, VerticalAlign } from "../build";
const doc = new Document();
const table = doc.createTable(2, 2);
const table = doc.createTable({
rows: 2,
columns: 2,
});
table
.getCell(1, 1)
.addContent(new Paragraph("This text should be in the middle of the cell"))
.CellProperties.setVerticalAlign(VerticalAlign.CENTER);
.addParagraph(new Paragraph("This text should be in the middle of the cell"))
.setVerticalAlign(VerticalAlign.CENTER);
table
.getCell(1, 0)
.addContent(
.addParagraph(
new Paragraph(
"Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah Blah",
).heading1(),

View File

@ -1,33 +1,99 @@
// Example of how you would create a table and add data to it
// Example of how you would merge cells together
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph } from "../build";
import { Document, Packer, Paragraph, ShadingType, WidthType } from "../build";
const doc = new Document();
let table = doc.createTable(2, 2);
let table = doc.createTable({
rows: 2,
columns: 2,
});
table.getCell(0, 0).addContent(new Paragraph("Hello"));
table.getCell(0, 0).addParagraph(new Paragraph("Hello"));
table.getRow(0).mergeCells(0, 1);
doc.createParagraph("Another table").heading2();
table = doc.createTable(2, 3);
table.getCell(0, 0).addContent(new Paragraph("World"));
table = doc.createTable({
rows: 2,
columns: 3,
width: 100,
widthUnitType: WidthType.AUTO,
columnWidths: [1000, 1000, 1000],
});
table
.getCell(0, 0)
.addParagraph(new Paragraph("World"))
.setMargins({
top: 1000,
bottom: 1000,
left: 1000,
right: 1000,
});
table.getRow(0).mergeCells(0, 2);
doc.createParagraph("Another table").heading2();
table = doc.createTable(2, 4);
table.getCell(0, 0).addContent(new Paragraph("Foo"));
table = doc.createTable({
rows: 2,
columns: 4,
width: 7000,
widthUnitType: WidthType.DXA,
margins: {
top: 400,
bottom: 400,
right: 400,
left: 400,
},
});
table.getCell(0, 0).addParagraph(new Paragraph("Foo"));
table.getCell(0, 1).addParagraph(new Paragraph("v"));
table.getCell(1, 0).addContent(new Paragraph("Bar1"));
table.getCell(1, 1).addContent(new Paragraph("Bar2"));
table.getCell(1, 2).addContent(new Paragraph("Bar3"));
table.getCell(1, 3).addContent(new Paragraph("Bar4"));
table
.getCell(1, 0)
.addParagraph(new Paragraph("Bar1"))
.setShading({
fill: "b79c2f",
val: ShadingType.REVERSE_DIAGONAL_STRIPE,
color: "auto",
});
table
.getCell(1, 1)
.addParagraph(new Paragraph("Bar2"))
.setShading({
fill: "42c5f4",
val: ShadingType.PERCENT_95,
color: "auto",
});
table
.getCell(1, 2)
.addParagraph(new Paragraph("Bar3"))
.setShading({
fill: "880aa8",
val: ShadingType.PERCENT_10,
color: "e2df0b",
});
table
.getCell(1, 3)
.addParagraph(new Paragraph("Bar4"))
.setShading({
fill: "FF0000",
val: ShadingType.CLEAR,
color: "auto",
});
table.getRow(0).mergeCells(0, 3);
doc.createParagraph("hi");
doc.createTable({
rows: 2,
columns: 2,
width: 100,
widthUnitType: WidthType.PERCENTAGE,
});
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {

View File

@ -1,28 +1,25 @@
// Example of how you would create a table with float positions
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import {
Document,
Packer,
Paragraph,
RelativeHorizontalPosition,
RelativeVerticalPosition,
TableAnchorType,
WidthType,
} from "../build";
import { Document, Packer, Paragraph, RelativeHorizontalPosition, RelativeVerticalPosition, TableAnchorType, WidthType } from "../build";
const doc = new Document();
const table = doc.createTable(2, 2).float({
horizontalAnchor: TableAnchorType.MARGIN,
verticalAnchor: TableAnchorType.MARGIN,
relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT,
relativeVerticalPosition: RelativeVerticalPosition.BOTTOM,
const table = doc.createTable({
rows: 2,
columns: 2,
float: {
horizontalAnchor: TableAnchorType.MARGIN,
verticalAnchor: TableAnchorType.MARGIN,
relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT,
relativeVerticalPosition: RelativeVerticalPosition.BOTTOM,
},
width: 4535,
widthUnitType: WidthType.DXA,
});
table.setFixedWidthLayout();
table.setWidth(WidthType.DXA, 4535);
table.getCell(0, 0).addContent(new Paragraph("Hello"));
table.getCell(0, 0).addParagraph(new Paragraph("Hello"));
table.getRow(0).mergeCells(0, 1);
const packer = new Packer();

18
demo/demo35.ts Normal file
View File

@ -0,0 +1,18 @@
// Example on how to add hyperlinks to websites
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph } from "../build";
const doc = new Document();
const paragraph = new Paragraph();
const link = doc.createHyperlink("http://www.example.com", "Hyperlink");
link.bold();
paragraph.addHyperLink(link);
doc.addParagraph(paragraph);
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

26
demo/demo36.ts Normal file
View File

@ -0,0 +1,26 @@
// Add image to table cell
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Media, Packer, Table } from "../build";
const doc = new Document();
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
const table = new Table({
rows: 2,
columns: 2,
});
table.getCell(1, 1).addParagraph(image.Paragraph);
// doc.createParagraph("Hello World");
doc.addTable(table);
// doc.Header.createImage(fs.readFileSync("./demo/images/pizza.gif"));
doc.Header.addTable(table);
// doc.Footer.createImage(fs.readFileSync("./demo/images/pizza.gif"));
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

18
demo/demo37.ts Normal file
View File

@ -0,0 +1,18 @@
// Add images to header and footer
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Media, Packer } from "../build";
const doc = new Document();
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
doc.createParagraph("Hello World");
doc.Header.addImage(image);
doc.Header.createImage(fs.readFileSync("./demo/images/pizza.gif"));
doc.Header.createImage(fs.readFileSync("./demo/images/image1.jpeg"));
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

View File

@ -1,4 +1,4 @@
// Example of how to add images to the document - You can use Buffers, UInt8Arrays or Base64 strings
// Example of how to "wrap" text around an image
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
// import { Document, Packer, Paragraph } from "../build";

View File

@ -1,4 +1,4 @@
// Scaling images
// Example how to display page numbers
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, PageNumberFormat, TextRun } from "../build";

View File

@ -5,8 +5,11 @@ import { Document, Packer, Paragraph } from "../build";
const doc = new Document();
const table = doc.createTable(4, 4);
table.getCell(2, 2).addContent(new Paragraph("Hello"));
const table = doc.createTable({
rows: 4,
columns: 4,
});
table.getCell(2, 2).addParagraph(new Paragraph("Hello"));
const packer = new Packer();

26
demo/demo40.ts Normal file
View File

@ -0,0 +1,26 @@
// Example demonstrating line numbers.
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, LineNumberRestartFormat, Packer, Paragraph } from "../build";
const doc = new Document(
{},
{
lineNumberCountBy: 1,
lineNumberRestart: LineNumberRestartFormat.CONTINUOUS,
},
);
doc.addParagraph(new Paragraph("Hello").heading1());
doc.createParagraph(
"Himenaeos duis luctus nullam fermentum lobortis potenti vivamus non dis, sed facilisis ultricies scelerisque aenean risus hac senectus. Adipiscing id venenatis justo ante gravida placerat, ac curabitur dis pellentesque proin bibendum risus, aliquam porta taciti vulputate primis. Tortor ipsum fermentum quam vel convallis primis nisl praesent tincidunt, lobortis quisque felis vitae condimentum class ut sem nam, aenean potenti pretium ac amet lacinia himenaeos mi. Aliquam nisl turpis hendrerit est morbi malesuada, augue interdum mus inceptos curabitur tristique, parturient feugiat sodales nulla facilisi. Aliquam non pulvinar purus nulla ex integer, velit faucibus vitae at bibendum quam, risus elit aenean adipiscing posuere.",
);
doc.createParagraph(
"Sed laoreet id mattis egestas nam mollis elit lacinia convallis dui tincidunt ultricies habitant, pharetra per maximus interdum neque tempor risus efficitur morbi imperdiet senectus. Lectus laoreet senectus finibus inceptos donec potenti fermentum, ultrices eleifend odio suscipit magnis tellus maximus nibh, ac sit nullam eget felis himenaeos. Diam class sem magnis aenean commodo faucibus id proin mi, nullam sodales nec mus parturient ornare ad inceptos velit hendrerit, bibendum placerat eleifend integer facilisis urna dictumst suspendisse.",
);
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

51
demo/demo41.ts Normal file
View File

@ -0,0 +1,51 @@
// Multiple cells merging in the same table
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph } from "../build";
const doc = new Document();
const table = doc.createTable({
rows: 13,
columns: 6,
});
let row = 0;
table.getCell(row, 0).addContent(new Paragraph("0,0"));
table.getCell(row, 1).addContent(new Paragraph("0,1"));
table.getCell(row, 3).addContent(new Paragraph("0,3"));
table.getCell(row, 4).addContent(new Paragraph("0,4"));
table.getRow(row).mergeCells(4, 5);
table.getRow(row).mergeCells(1, 2);
row = 1;
table.getCell(row, 0).addContent(new Paragraph("1,0"));
table.getCell(row, 2).addContent(new Paragraph("1,2"));
table.getCell(row, 4).addContent(new Paragraph("1,4"));
table.getRow(row).mergeCells(4, 5);
table.getRow(row).mergeCells(2, 3);
table.getRow(row).mergeCells(0, 1);
row = 2;
table.getCell(row, 0).addContent(new Paragraph("2,0"));
table.getCell(row, 1).addContent(new Paragraph("2,1"));
table.getCell(row, 2).addContent(new Paragraph("2,2"));
table.getCell(row, 3).addContent(new Paragraph("2,3"));
table.getCell(row, 4).addContent(new Paragraph("2,4"));
table.getRow(row).mergeCells(4, 5);
table.getRow(row).mergeCells(1, 2);
row = 3;
table.getCell(row, 0).addContent(new Paragraph("3,0"));
table.getCell(row, 1).addContent(new Paragraph("3,1"));
table.getCell(row, 2).addContent(new Paragraph("3,2"));
table.getCell(row, 3).addContent(new Paragraph("3,3"));
table.getCell(row, 4).addContent(new Paragraph("3,4"));
table.getCell(row, 5).addContent(new Paragraph("3,5"));
row = 4;
table.getCell(row, 0).addContent(new Paragraph("4,0"));
table.getCell(row, 5).addContent(new Paragraph("4,5"));
table.getRow(row).mergeCells(0, 4);
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

20
demo/demo43.ts Normal file
View File

@ -0,0 +1,20 @@
// Add image to table cell
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph } from "../build";
const doc = new Document();
const table = doc.createTable({
rows: 4,
columns: 4,
});
table.getCell(2, 2).addParagraph(new Paragraph("Hello"));
table.getColumn(3).mergeCells(1, 2);
// table.getCell(3, 2).addParagraph(new Paragraph("Hello"));
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

36
demo/demo44.ts Normal file
View File

@ -0,0 +1,36 @@
// Sections with multiple columns
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer } from "../build";
const doc = new Document();
doc.addSection({
column: {
width: 708,
count: 2,
},
});
doc.createParagraph("This text will be split into 2 columns on a page.");
doc.createParagraph(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
);
doc.addSection({
column: {
width: 708,
count: 2,
},
});
doc.createParagraph("This text will be split into 3 columns on a page.");
doc.createParagraph(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
);
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

View File

@ -29,6 +29,7 @@ import * as docx from "docx";
## Basic Usage
```js
var fs = require("fs");
var docx = require("docx");
// Create document
@ -41,11 +42,12 @@ paragraph.addRun(new docx.TextRun("Lorem Ipsum Foo Bar"));
doc.addParagraph(paragraph);
// Used to export the file into a .docx file
var exporter = new docx.LocalPacker(doc);
var packer = new docx.Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My First Document.docx", buffer);
});
exporter.pack("My First Document");
// Done! A file called 'My First Document.docx' will be in your file system if you used LocalPacker
// Done! A file called 'My First Document.docx' will be in your file system.
```
## Honoured Mentions

View File

@ -15,6 +15,7 @@
* [Headers & Footers](usage/headers-and-footers.md)
* [Bullet Points](usage/bullet-points.md)
* [Numbering](usage/numbering.md)
* [Tables](usage/tables.md)
* [Tab Stops](usage/tab-stops.md)
* [Table of Contents](usage/table-of-contents.md)
* [Page Numbers](usage/page-numbers.md)

View File

@ -12,13 +12,13 @@
## Always think about the user
The number one pillar for contribution is to **ALWAYS** think about how the user will use the library.
The number one pillar for contribution to `docx` is to **ALWAYS** think about how the user will use `docx`.
Put yourself in their position, and imagine how they would feel about your feature you wrote.
1. Is it easy to use?
2. Has it been documented well?
3. Is it intuative?
3. Is it intuitive?
4. Is it consistent with the rest of the API?
5. Is it fun to use?
@ -37,13 +37,13 @@ Unesesary coment removed // Make sure to use correct spelling
## No leaky components in API interface
This mainly applies to the API the end user will consume.
> This mainly applies to the API the end user will consume.
Try to make method parameters accept primatives, or `json` objects, so that child components are created **inside** the component, rather than being **injected** in.
Try to make method parameters of the outside API accept primitives, or `json` objects, so that child components are created **inside** the component, rather than being **injected** in.
This is so that:
1. Imports are much cleaner, no need for:
1. Imports are much cleaner for the end user, no need for:
```js
import { ChildComponent } from "./my-feature/sub-component/deeper/.../my-deep.component";
```
@ -52,17 +52,29 @@ This is so that:
3. It means the end user does not need to import and create the child component to be injected.
**Do not**
`TableFloatProperties` is a class. The outside world would have to construct the object, and inject it in
`TableFloatProperties` is a class. The outside world would have to `new` up the object, and inject it in like so:
```js
public float(tableFloatProperties: TableFloatProperties): Table
```
```ts
table.float(new TableFloatProperties(...));
```
**Do**
`ITableFloatOptions` is an interface for a JSON of primatives.
`ITableFloatOptions` is an interface for a JSON of primitives. The end user would need to pass in a json object and not need to worry about the internals:
```js
public float(tableFloatOptions: ITableFloatOptions): Table
```
```ts
table.float({...});
```
## Add vs Create
This is just a guideline, and the rules can sometimes be broken.
@ -76,7 +88,8 @@ This is just a guideline, and the rules can sometimes be broken.
}
```
* Use `add` if you add the element into the method as a parameter:
* Use `add` if you add the element into the method as a parameter.
*Note:* This may look like its breaking the previous guideline, but it has semantically different meanings. The previous one is using data to construct an object, whereas this one is simply adding elements into the document:
```js
public addParagraph(paragraph: Paragraph) {
@ -108,6 +121,30 @@ private get _level: string;
private get level: string;
```
## Temporal Methods
Some methods are `non-temporal`, which means regardless of when you call the method, it will have the same affect on the document. For example, setting the width of a table at the end of the document will have the same effect as setting the width at the start:
```ts
table.setWidth(1000); // now removed as of version 5.0.0
```
Whereas some methods are `temporal`, which means depending on the time-frame they are called, it would produce a difference result. For example, moving `createParagraph()` around your code will physically alter the document.
```ts
doc.createParagraph("hello");
```
If a method is `non-temporal`, put it in the objects `constructor`. For example:
```ts
const table = new Table(width: number);
```
`Non-temporal` methods are usually methods which can only be used one time and one time only. For example, `.float()`. It does not make sense to call `.float()` again if its already floating.
I am not sure what the real term is, but this will do.
## Interfaces over type alias
Do not use `type`, but rather use `Interfaces`. `type` cannot be extended, and a class cannot implement it.
@ -154,7 +191,7 @@ enum WeaponType = {
}
```
## Spell correctly, full and in American English
## Spell correctly, in full and in American English
I am not sure where these habits in software development come from, but I do not believe it is beneficial:

View File

@ -14,7 +14,7 @@ This command will run the `demo selector app` in the `/demo` folder. It will pro
A simple hello world of the `docx` library:
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo1.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo1.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo1.ts_
@ -24,7 +24,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo1.ts_
This example shows how to customise the look and feel of a document using JS configuration
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo2.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo2.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo2.ts_
@ -32,7 +32,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo2.ts_
This example shows how to customise the look and feel of a document using XML configuration
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo13.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo13.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo13.ts_
@ -40,7 +40,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo13.ts_
This example shows many levels of numbering
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo3.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo3.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo3.ts_
@ -48,7 +48,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo3.ts_
Example of simple table
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_
@ -56,7 +56,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_
Styling the borders of a table
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_
@ -66,7 +66,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_
Importing Images from file system path
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
@ -74,7 +74,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
Example showing how to add image to headers and footers
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
@ -82,7 +82,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
Example showing how to scale images
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo12.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo12.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo12.ts_
@ -90,7 +90,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo12.ts_
This is the best way to add an image to a document because you can add the same image in two locations without increasing document size by re-using the same image
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo23.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo23.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo23.ts_
@ -98,7 +98,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo23.ts_
As before, to add an image to a table, you would need to add it to the `Media` object first
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_
@ -106,15 +106,15 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_
If you want to use a Base64 image instead
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo18.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo18.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo18.ts_
## Margins
Example showing how to set custom margains
Example showing how to set custom margins
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo6.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo6.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo6.ts_
@ -122,7 +122,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo6.ts_
Example showing how to set the document to `landscape` or `portrait`
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo7.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo7.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo7.ts_
@ -130,7 +130,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo7.ts_
Example showing how to add headers and footers
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo8.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo8.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo8.ts_
@ -144,7 +144,7 @@ Check out `Sections` for this feature
Example showing how to page break
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo14.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo14.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo14.ts_
@ -152,15 +152,16 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo14.ts_
Example showing how to page break before like in Word
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo15.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo15.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo15.ts_
## Sections
Example of how sections work. Sections allow multiple headers and footers, and `landscape`/`portrait` inside the same document
Example of how sections work. Sections allow multiple headers and footers, and `landscape`/`portrait` inside the same document.
Also you can have different page number formats and starts for different sections.
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo16.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo16.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo16.ts_
@ -168,7 +169,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo16.ts_
Example of how to add footnotes. Good for references
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo17.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo17.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo17.ts_
@ -178,7 +179,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo17.ts_
Example showing how to use the Buffer packer and then write that buffer to the file system
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo19.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo19.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo19.ts_
@ -187,7 +188,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo19.ts_
Example showing how to make bookmarks to make internal hyperlinks within the document
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo21.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo21.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo21.ts_
@ -195,7 +196,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo21.ts_
Example showing how to use bidirectional text for certain languages such as Hebrew
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo22.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo22.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo22.ts_
@ -205,7 +206,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo22.ts_
Example showing how to add headers and footers
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo10.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo10.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo10.ts_
@ -213,6 +214,6 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo10.ts_
This example shows how to customise the look and feel of a document and add images
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo11.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo11.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo11.ts_

View File

@ -25,6 +25,7 @@
<script src="//unpkg.com/docsify/lib/plugins/emoji.min.js"></script>
<script src="https://unpkg.com/docsify-copy-code@2"></script>
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
<script src="//unpkg.com/prismjs/components/prism-typescript.min.js"></script>
</body>
</html>

View File

@ -5,7 +5,7 @@
To create a new document, it is very easy:
```js
var doc = new docx.Document();
const doc = new docx.Document();
```
## Document properties
@ -13,7 +13,7 @@ var doc = new docx.Document();
You can add properties to the Word document by specifying options, for example:
```js
var doc = new docx.Document({
const doc = new docx.Document({
creator: "Dolan Miu",
description: "My extremely interesting document",
title: "My Document",
@ -22,14 +22,18 @@ var doc = new docx.Document({
### Full list of options:
```
creator
description
title
subject
keywords
lastModifiedBy
revision
```
* creator
* description
* title
* subject
* keywords
* lastModifiedBy
* revision
You can mix and match whatever properties you want, or provide no properties.
### units for positioning
Various parts of the API require positioning arguments. The units are "20ths of a point" from the [OOXML](http://officeopenxml.com/index.php) specification.
See [Lars Corneliussen's blog post](https://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/) for more information and how to convert units.

View File

@ -23,7 +23,7 @@ doc.Header.createImage([BUFFER_OF_YOUR_IMAGE]);
doc.Footer.createImage([BUFFER_OF_YOUR_IMAGE]);
```
Refer to `demo8.js` for more information
Refer to [`demo8.ts`](https://github.com/dolanmiu/docx/blob/master/demo/demo8.ts) for more information.
## Multiple Headers and Footers
@ -37,8 +37,12 @@ Also all the supported section properties are implemented according to: http://o
// Add new section with another header and footer
doc.addSection({
headerId: header.Header.ReferenceId,
footerId: footer.Footer.ReferenceId,
headers: {
default: header
},
footers: {
default: footer
},
pageNumberStart: 1,
pageNumberFormatType: docx.PageNumberFormat.DECIMAL,
});

47
docs/usage/hyperlinks.md Normal file
View File

@ -0,0 +1,47 @@
# Hyperlinks
There are two types of hyperlinks: internal (pointing to a bookmark inside the document) and external (pointing to an external url).
## Internal
To create an internal hyperlink you need first to create a bookmark (the paragraph that will be the destination of the hyperlink) with `doc.createBookmark(anchor, text)`.
A bookmark is composed of an anchor (an identifier) and the text displayed. After creating a bookmark just add it to a paragraph with `paragraph.addBookmark(bookmark)`
For example:
```ts
const paragraph = this.doc.createParagraph();
const bookmark = this.doc.createBookmark('anchorForChapter1', 'This is chapter1');
paragraph.addBookmark(bookmark);
```
Then you can create an hyperlink pointing to that bookmark with `doc.createInternalHyperLink(anchor,text)`:
```ts
const paragraph = this.doc.createParagraph();
const link = this.doc.createInternalHyperLink('anchorForChapter1', 'This is a link to chapter1');
paragraph.addHyperLink(link);
```
## External
To create an external hyperlink you just need to specify the url and the text of the link, then add it to a paragraph with `doc.createHyperlink(url, text)`:
```ts
const paragraph = this.doc.createParagraph();
const link = this.doc.createHyperlink('https://docx.js.org', 'This is an external link');
paragraph.addHyperLink(link);
```
## Styling an hyperlink
It is possible to set the style of the text of an hyperlink. This can be done applying run formatting on `TextRun` property of the hyperlink.
Example:
```ts
const link = this.doc.createHyperlink('https://docx.js.org', 'This is an external link');
link.TextRun.bold().italics()
```

View File

@ -210,7 +210,7 @@ doc.createImage(fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
Importing Images from file system path
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
@ -218,7 +218,7 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
Example showing how to add image to headers and footers
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
@ -226,6 +226,6 @@ _Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
Example showing how to float images on top of text and optimally give a `margin`
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo38.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo38.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo38.ts_

View File

@ -26,7 +26,7 @@ _levels_ which form a sequence starting at 0 indicating the top-level
list look and increasing from there to descibe the sublists, then
sub-sublists, etc. Each level includes the following properties:
* **level**: This its 0-based index in the defintion stack
* **level**: This is its 0-based index in the definition stack
* **numberFormat**: This indicates how the bullet or number should be
generated. Options include `bullet` (meaning don't count), `decimal`
(arabic numerals), `upperRoman`, `lowerRoman`, `hex`, and many
@ -64,8 +64,8 @@ styles or numbering of any kind, you'll have to use the
`docx.Numbering` class.
First you need to create a new numbering container class and use it to
create your abstract numbering style, define your levels, and creat
your concreate numbering style:
create your abstract numbering style, define your levels, and create
your concrete numbering style:
```js
const numbering = new docx.Numbering();
@ -78,8 +78,8 @@ abstractNum.createLevel(2, "lowerLetter", "%3)", "start").addParagraphProperty(n
const concrete = numbering.createConcreteNumbering(abstractNum);
```
You can then apply your concrete style to paragraphs using their
`#setNumbering` method:
You can then apply your concrete style to paragraphs using the
`setNumbering` method:
```js
topLevelP.setNumbering(concrete, 0);

View File

@ -61,6 +61,6 @@ doc.Header.createParagraph().addRun(new TextRun("Page ").pageNumber()).addRun(ne
Adding page numbers to Header and Footer
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo39.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo39.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo39.ts_

View File

@ -76,6 +76,22 @@ paragraph.heading1().center();
The above will create a `heading 1` which is `centered`.
### Justified text with breaks
When a paragraph is justified, you may want to not justify the contents of incomplete lines, which end in a soft line break.
![Justified line before](https://user-images.githubusercontent.com/7989576/53820338-e060c680-3f6b-11e9-817c-ecb43271951e.png)
This is possible to achieve using:
```ts
this.doc.Settings.addCompatibility().doNotExpandShiftReturn();
```
The result is:
![Justified line after](https://user-images.githubusercontent.com/7989576/53820344-e2c32080-3f6b-11e9-9afe-24a2ed6e0d78.png)
## Thematic Break
To add a break in the page, simply add `.thematicBreak()` on a paragraph:
@ -106,7 +122,7 @@ var paragraph = new docx.Paragraph("Hello World on another page").pageBreakBefor
![Page Break Before in Word](https://user-images.githubusercontent.com/34742290/40176503-df3a8398-59db-11e8-8b9c-d719f13aa8b4.png)
Example: https://github.com/dolanmiu/docx/blob/master/demo/demo15.js
Example: https://github.com/dolanmiu/docx/blob/master/demo/demo15.ts
## Page break control

View File

@ -14,7 +14,7 @@ const name = new TextRun("Name:")
## Available methods
* For run formatting:
* `.bold()`, `.italic()`, `.smallCaps()`, `.allCaps()`, `.strike()`, `.doubleStrike()`, `.subScript()`, `.superScript()`: Set the formatting property to true
* `.bold()`, `.italics()`, `.smallCaps()`, `.allCaps()`, `.strike()`, `.doubleStrike()`, `.subScript()`, `.superScript()`: Set the formatting property to true
* `.underline(style="single", color=null)`: Set the underline style and color
* `.color(color)`: Set the text color, using 6 hex characters for RRGGBB (no leading `#`)
* `.size(halfPts)`: Set the font size, measured in half-points

View File

@ -44,4 +44,4 @@ doc.addParagraph(paragraph);
doc.createParagraph("Some normal text");
```
Example: https://github.com/dolanmiu/docx/blob/master/demo/demo13.js
Example: https://github.com/dolanmiu/docx/blob/master/demo/demo13.ts

View File

@ -2,7 +2,7 @@
> Tab stops are useful, if you are unclear of what they are, [here is a link explaining](https://en.wikipedia.org/wiki/Tab_stop). It enables side by side text which is nicely laid out without the need for tables, or constantly pressing space bar.
**Note**: At the moment, the unit of measurement for a tab stop is counter intuitive for a human. It is using OpenXMLs own measuring system. For example, 2268 roughly translates to 3cm. Therefore in the future, I may consider changing it to percentages or even cm.
!> **Note**: At the moment, the unit of measurement for a tab stop is counter intuitive for a human. It is using OpenXMLs own measuring system. For example, 2268 roughly translates to 3cm. Therefore in the future, I may consider changing it to percentages or even cm.
![Word 2013 Tabs](http://www.teachucomp.com/wp-content/uploads/blog-4-22-2015-UsingTabStopsInWord-1024x577.png "Word 2013 Tab Stops")

View File

@ -71,6 +71,6 @@ doc.addParagraph(new Paragraph("My Spectacular Style #1").style("MySpectacularSt
### Complete example
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo28.ts ":include")
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo28.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo28.ts_

305
docs/usage/tables.md Normal file
View File

@ -0,0 +1,305 @@
# Tables
You can create tables with `docx`. More information can be found [here](http://officeopenxml.com/WPtable.php).
## Create Table
To create a table, simply use the `createTable()` method on a `document`.
```ts
const table = doc.createTable([NUMBER OF ROWS], [NUMBER OF COLUMNS]);
```
Alternatively, you can create a table object directly, and then add it in the `document`
```ts
const table = new Table(4, 4);
doc.addTable(table);
```
The snippet below creates a table of 2 rows and 4 columns.
```ts
const table = doc.createTable(2, 4);
// Or
const table = new Table(2, 4);
doc.addTable(table);
```
## Rows and Columns
You can get a row or a column from a table like so, where `index` is a number:
### Get Row
```ts
const row = doc.getRow(index);
```
With this, you can merge a row by using the `mergeCells()` method, where `startIndex` is the row number you want to merge from, and `endIndex` is where you want it to merge to:
```ts
row.mergeCells(startIndex, endIndex);
```
You can get a cell from a `row` by using the `getCell()` method, where `index` is the row index:
```ts
row.getCell(index);
```
### Get Column
```ts
const column = doc.getColumn(index);
```
Again, you can merge a row by using the `mergeCells()` method, where `startIndex` is the row number you want to merge from, and `endIndex` is where you want it to merge to:
```ts
column.mergeCells(startIndex, endIndex);
```
You can get a cell from a `column` by using the `getCell()` method, where `index` is the column index:
```ts
column.getCell(index);
```
## Cells
The `createTable()` method created a table with cells. To access the cell, use the `getCell()` method.
```ts
const cell = table.getCell([ROW INDEX], [COLUMN INDEX]);
```
You can also get a cell from a `column` or a `row` with `getCell()`, mentioned previously.
For example:
```ts
const cell = table.getCell(0, 2);
const cell = row.getCell(0);
const cell = column.getCell(2);
```
### Add paragraph to a cell
Once you have got the cell, you can add data to it with the `addParagraph()` method.
```ts
cell.addParagraph(new Paragraph("Hello"));
```
### Set width of a cell
You can specify the width of a cell using:
`cell.Properties.setWidth(width, format)`
format can be:
* WidthType.AUTO
* WidthType.DXA: value is in twentieths of a point
* WidthType.NIL: is considered as zero
* WidthType.PCT: percent of table width
### Example
```ts
cell.Properties.setWidth(100, WidthType.DXA);
```
```ts
cell.Properties.setWidth('50%', WidthType.PCT);
```
## Borders
BorderStyle can be imported from `docx`. Size determines the thickness. HTML color can be a hex code or alias such as `red`.
```ts
cell.Borders.addTopBorder([BorderStyle], [SIZE], [HTML COLOR]);
```
```ts
cell.Borders.addBottomBorder([BorderStyle], [SIZE], [HTML COLOR]);
```
```ts
cell.Borders.addStartBorder([[BorderStyle]], [SIZE], [HTML COLOR]);
```
```ts
cell.Borders.addEndBorder([BorderStyle], [SIZE], [HTML COLOR]);
```
### Example
```ts
import { BorderStyle } from "docx";
cell.Borders.addStartBorder(BorderStyle.DOT_DOT_DASH, 3, "green");
cell.Borders.addEndBorder(BorderStyle.DOT_DOT_DASH, 3, "#ff8000");
```
### Google DOCS
Google DOCS does not support start and end borders, instead they use left and right borders. So to set left and right borders for Google DOCS you should use:
```ts
import { BorderStyle } from "docx";
cell.Borders.addLeftBorder(BorderStyle.DOT_DOT_DASH, 3, "green");
cell.Borders.addRightBorder(BorderStyle.DOT_DOT_DASH, 3, "#ff8000");
```
## Set Width
```ts
import { WidthType } from "docx";
table.setWidth([WIDTH], [OPTIONAL WidthType. Defaults to DXA]);
```
For example:
```ts
table.setWidth(4535, WidthType.DXA);
```
## Vertical Align
Sets the vertical alignment of the contents of the cell
```ts
import { VerticalAlign } from "docx";
cell.setVerticalAlign([VerticalAlign TYPE]);
```
For example, to center align a cell:
```ts
cell.setVerticalAlign(VerticalAlign.CENTER);
```
## Rows
To get a row, use the `getRow` method on a `table`. There are a handful of methods which you can apply to a row which will be explained below.
```ts
table.getRow([ROW INDEX]);
```
## Merge cells together
### Merging on a row
First obtain the row, and call `mergeCells()`. The first argument is where the merge should start. The second argument is where the merge should end.
```ts
table.getRow(0).mergeCells([FROM INDEX], [TO INDEX]);
```
#### Example
This will merge 3 cells together starting from index `0`:
```ts
table.getRow(0).mergeCells(0, 2);
```
### Merging on a column
It has not been implemented yet, but it will follow a similar structure as merging a row.
## Nested Tables
To have a table within a table
```ts
cell.addTable(new Table(1, 1));
```
## Pagination
###Prevent row pagination
To prevent breaking contents of a row across multiple pages, call `cantSplit()`:
```ts
table.getRow(0).setCantSplit();
```
###Repeat row
If a table is paginated on multiple pages, it is possible to repeat a row at the top of each new page calling `setTableHeader()`:
```ts
table.getRow(0).setTableHeader();
```
## Examples
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo4.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo4.ts_
### Custom borders
Example showing how to add colourful borders to tables
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo20.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo20.ts_
### Adding images
Example showing how to add images to tables
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo24.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo24.ts_
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo36.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo36.ts_
### Alignment of text in a cell
Example showing how align text in a table cell
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo31.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo31.ts_
### Merging rows
Example showing merging of `rows`
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo32.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo32.ts_
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo41.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo41.ts_
### Merging columns
Example showing merging of `columns`
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo43.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo43.ts_
### Floating tables
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo34.ts ':include')
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo34.ts_

View File

@ -22,7 +22,7 @@ text.bold();
### Italics
```js
text.italic();
text.italics();
```
### Underline
@ -80,5 +80,5 @@ text.break();
What if you want to create a paragraph which is **_bold_** and **_italic_**?
```js
paragraph.bold().italic();
paragraph.bold().italics();
```

View File

@ -1,6 +1,6 @@
{
"name": "docx",
"version": "4.7.0",
"version": "5.0.0-rc4",
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
"main": "build/index.js",
"scripts": {
@ -8,7 +8,7 @@
"test": "mocha-webpack \"src/**/*.ts\"",
"test.coverage": "nyc npm test",
"test.watch": "npm test -- --watch",
"prepublishOnly": "npm run build",
"prepublishOnly": "npm run build --production",
"lint": "tslint --project .",
"build": "npm run webpack && npm run fix-types",
"tsc": "rimraf ./build && tsc -p .",
@ -17,7 +17,9 @@
"typedoc": "typedoc src/index.ts",
"style": "prettier -l \"src/**/*.ts\"",
"style.fix": "npm run style -- --write",
"fix-types": "node types-absolute-fixer.js",
"fix-types": "ts-node scripts/types-absolute-fixer.ts",
"e2e": "ts-node scripts/e2e.ts",
"serve.docs": "cd docs && docsify serve",
"ts-node": "ts-node"
},
"pre-commit": [
@ -47,9 +49,7 @@
],
"types": "./build/index.d.ts",
"dependencies": {
"@types/image-size": "0.0.29",
"@types/jszip": "^3.1.4",
"image-size": "^0.6.2",
"jszip": "^3.1.5",
"xml": "^1.0.1",
"xml-js": "^1.6.8"
@ -63,9 +63,12 @@
"devDependencies": {
"@types/chai": "^3.4.35",
"@types/mocha": "^2.2.39",
"@types/request-promise": "^4.1.42",
"@types/sinon": "^4.3.1",
"@types/webpack": "^4.4.24",
"awesome-typescript-loader": "^3.4.1",
"chai": "^3.5.0",
"docsify": "^4.9.1",
"glob": "^7.1.2",
"istanbul-instrumenter-loader": "^3.0.1",
"jszip": "^3.1.5",
@ -73,9 +76,11 @@
"mocha-webpack": "^1.0.1",
"nyc": "^13.1.0",
"pre-commit": "^1.2.2",
"prettier": "^1.12.1",
"prettier": "^1.15.2",
"prompt": "^1.0.0",
"replace-in-file": "^3.1.0",
"request": "^2.88.0",
"request-promise": "^4.2.2",
"rimraf": "^2.5.2",
"shelljs": "^0.7.7",
"sinon": "^5.0.7",

30
scripts/e2e.ts Normal file
View File

@ -0,0 +1,30 @@
// tslint:disable:no-console
import * as fs from "fs";
import * as request from "request-promise";
async function e2e(filePath: string): Promise<void> {
console.log(`Running e2e for: ${filePath}`);
if (!fs.existsSync(filePath)) {
console.error("File not found");
throw Error("File not found");
}
const result = await request.post({
url: "https://wt-9017166451e5dc00461b648d19f5e8da-0.sandbox.auth0-extend.com/docx-validator",
formData: {
document: fs.createReadStream(filePath),
},
});
return result;
}
e2e(process.argv[2])
.then(() => {
console.log("Success! Document is valid");
})
.catch(() => {
console.log("Error! Validation failed");
process.exit(1);
});

View File

@ -1,5 +1,5 @@
const glob = require("glob");
const replace = require("replace-in-file");
import * as glob from "glob";
import * as replace from "replace-in-file";
const files = glob.sync("build/**/*.d.ts");
@ -14,7 +14,7 @@ for (const file of files) {
.fill("../")
.join("");
return `"${backLevels}${matchSlug}"`;
return `"${backLevels}${matchSlug}"`;
},
});
}

View File

@ -1,10 +1,9 @@
import { assert } from "chai";
import { assert, expect } from "chai";
import { Formatter } from "../export/formatter";
import * as file from "../file";
import { CoreProperties } from "../file/core-properties";
import { Attributes } from "../file/xml-components";
import { Utility } from "../tests/utility";
import { Formatter } from "export/formatter";
import * as file from "file";
import { CoreProperties } from "file/core-properties";
import { Attributes } from "file/xml-components";
describe("Formatter", () => {
let formatter: Formatter;
@ -31,33 +30,31 @@ describe("Formatter", () => {
const paragraph = new file.Paragraph();
paragraph.addRun(new file.TextRun("test").bold());
const newJson = formatter.format(paragraph);
assert.isDefined(newJson["w:p"][1]["w:r"][0]["w:rPr"][0]["w:b"][0]._attr["w:val"]);
assert.isDefined(newJson["w:p"][0]["w:r"][0]["w:rPr"][0]["w:b"]._attr["w:val"]);
});
it("should format attributes (rsidSect)", () => {
const attributes = new Attributes({
rsidSect: "test2",
});
let newJson = formatter.format(attributes);
newJson = Utility.jsonify(newJson);
if (newJson._attr === undefined) {
assert.fail();
return;
}
assert.isDefined(newJson._attr["w:rsidSect"]);
const tree = formatter.format(attributes);
expect(tree).to.deep.equal({
_attr: {
"w:rsidSect": "test2",
},
});
});
it("should format attributes (val)", () => {
const attributes = new Attributes({
val: "test",
});
let newJson = formatter.format(attributes);
newJson = Utility.jsonify(newJson);
if (newJson._attr === undefined) {
assert.fail();
return;
}
assert.isDefined(newJson._attr["w:val"]);
const tree = formatter.format(attributes);
expect(tree).to.deep.equal({
_attr: {
"w:val": "test",
},
});
});
it("should should change 'p' tag into 'w:p' tag", () => {

View File

@ -0,0 +1,17 @@
import { IMediaData, Media } from "file/media";
export class ImageReplacer {
public replace(xmlData: string, mediaData: IMediaData[], offset: number): string {
let currentXmlData = xmlData;
mediaData.forEach((image, i) => {
currentXmlData = currentXmlData.replace(`{${image.fileName}}`, (offset + i).toString());
});
return currentXmlData;
}
public getMediaData(xmlData: string, media: Media): IMediaData[] {
return media.Array.filter((image) => xmlData.search(`{${image.fileName}}`) > 0);
}
}

View File

@ -17,7 +17,7 @@ describe("Compiler", () => {
describe("#compile()", () => {
it("should pack all the content", async function() {
this.timeout(99999999);
const zipFile = await compiler.compile(file);
const zipFile = compiler.compile(file);
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
expect(fileNames).is.an.instanceof(Array);
@ -46,7 +46,7 @@ describe("Compiler", () => {
this.timeout(99999999);
const zipFile = await compiler.compile(file);
const zipFile = compiler.compile(file);
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
expect(fileNames).is.an.instanceof(Array);

View File

@ -3,6 +3,7 @@ import * as xml from "xml";
import { File } from "file";
import { Formatter } from "../formatter";
import { ImageReplacer } from "./image-replacer";
interface IXmlifyedFile {
readonly data: string;
@ -28,14 +29,17 @@ interface IXmlifyedFileMapping {
export class Compiler {
private readonly formatter: Formatter;
private readonly imageReplacer: ImageReplacer;
private readonly prettifyXml?: boolean;
constructor() {
constructor(prettifyXml?: boolean) {
this.formatter = new Formatter();
this.imageReplacer = new ImageReplacer();
this.prettifyXml = prettifyXml;
}
public async compile(file: File): Promise<JSZip> {
public compile(file: File): JSZip {
const zip = new JSZip();
const xmlifiedFileMapping = this.xmlifyFile(file);
for (const key in xmlifiedFileMapping) {
@ -59,30 +63,43 @@ export class Compiler {
zip.file(`word/media/${data.fileName}`, mediaData);
}
for (const header of file.Headers) {
for (const data of header.Media.Array) {
zip.file(`word/media/${data.fileName}`, data.stream);
}
}
for (const footer of file.Footers) {
for (const data of footer.Media.Array) {
zip.file(`word/media/${data.fileName}`, data.stream);
}
}
return zip;
}
private xmlifyFile(file: File): IXmlifyedFileMapping {
file.verifyUpdateFields();
const documentRelationshipCount = file.DocumentRelationships.RelationshipCount + 1;
return {
Relationships: {
data: (() => {
const xmlData = xml(this.formatter.format(file.Document), this.prettifyXml);
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
mediaDatas.forEach((mediaData, i) => {
file.DocumentRelationships.createRelationship(
documentRelationshipCount + i,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`,
);
});
return xml(this.formatter.format(file.DocumentRelationships), this.prettifyXml);
})(),
path: "word/_rels/document.xml.rels",
},
Document: {
data: xml(this.formatter.format(file.Document), true),
data: (() => {
const tempXmlData = xml(this.formatter.format(file.Document), this.prettifyXml);
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, documentRelationshipCount);
return xmlData;
})(),
path: "word/document.xml",
},
Styles: {
data: xml(this.formatter.format(file.Styles)),
data: xml(this.formatter.format(file.Styles), this.prettifyXml),
path: "word/styles.xml",
},
Properties: {
@ -95,58 +112,85 @@ export class Compiler {
path: "docProps/core.xml",
},
Numbering: {
data: xml(this.formatter.format(file.Numbering)),
data: xml(this.formatter.format(file.Numbering), this.prettifyXml),
path: "word/numbering.xml",
},
Relationships: {
data: xml(this.formatter.format(file.DocumentRelationships)),
path: "word/_rels/document.xml.rels",
},
FileRelationships: {
data: xml(this.formatter.format(file.FileRelationships)),
data: xml(this.formatter.format(file.FileRelationships), this.prettifyXml),
path: "_rels/.rels",
},
Headers: file.Headers.map((headerWrapper, index) => ({
data: xml(this.formatter.format(headerWrapper.Header)),
path: `word/header${index + 1}.xml`,
})),
Footers: file.Footers.map((footerWrapper, index) => ({
data: xml(this.formatter.format(footerWrapper.Footer)),
path: `word/footer${index + 1}.xml`,
})),
HeaderRelationships: file.Headers.map((headerWrapper, index) => ({
data: xml(this.formatter.format(headerWrapper.Relationships)),
path: `word/_rels/header${index + 1}.xml.rels`,
})),
FooterRelationships: file.Footers.map((footerWrapper, index) => ({
data: xml(this.formatter.format(footerWrapper.Relationships)),
path: `word/_rels/footer${index + 1}.xml.rels`,
})),
HeaderRelationships: file.Headers.map((headerWrapper, index) => {
const xmlData = xml(this.formatter.format(headerWrapper.Header), this.prettifyXml);
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
mediaDatas.forEach((mediaData, i) => {
headerWrapper.Relationships.createRelationship(
i,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`,
);
});
return {
data: xml(this.formatter.format(headerWrapper.Relationships), this.prettifyXml),
path: `word/_rels/header${index + 1}.xml.rels`,
};
}),
FooterRelationships: file.Footers.map((footerWrapper, index) => {
const xmlData = xml(this.formatter.format(footerWrapper.Footer), this.prettifyXml);
const mediaDatas = this.imageReplacer.getMediaData(xmlData, file.Media);
mediaDatas.forEach((mediaData, i) => {
footerWrapper.Relationships.createRelationship(
i,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`,
);
});
return {
data: xml(this.formatter.format(footerWrapper.Relationships), this.prettifyXml),
path: `word/_rels/footer${index + 1}.xml.rels`,
};
}),
Headers: file.Headers.map((headerWrapper, index) => {
const tempXmlData = xml(this.formatter.format(headerWrapper.Header), this.prettifyXml);
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
// TODO: 0 needs to be changed when headers get relationships of their own
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
return {
data: xmlData,
path: `word/header${index + 1}.xml`,
};
}),
Footers: file.Footers.map((footerWrapper, index) => {
const tempXmlData = xml(this.formatter.format(footerWrapper.Footer), this.prettifyXml);
const mediaDatas = this.imageReplacer.getMediaData(tempXmlData, file.Media);
// TODO: 0 needs to be changed when headers get relationships of their own
const xmlData = this.imageReplacer.replace(tempXmlData, mediaDatas, 0);
return {
data: xmlData,
path: `word/footer${index + 1}.xml`,
};
}),
ContentTypes: {
data: xml(this.formatter.format(file.ContentTypes)),
data: xml(this.formatter.format(file.ContentTypes), this.prettifyXml),
path: "[Content_Types].xml",
},
AppProperties: {
data: xml(this.formatter.format(file.AppProperties)),
data: xml(this.formatter.format(file.AppProperties), this.prettifyXml),
path: "docProps/app.xml",
},
FootNotes: {
data: xml(this.formatter.format(file.FootNotes)),
data: xml(this.formatter.format(file.FootNotes), this.prettifyXml),
path: "word/footnotes.xml",
},
Settings: {
data: xml(this.formatter.format(file.Settings)),
data: xml(this.formatter.format(file.Settings), this.prettifyXml),
path: "word/settings.xml",
},
};
}
/* By default docx collapse empty tags. <a></a> -> <a/>. this function mimic it
so comparing (diff) original docx file and the library output is easier
Currently not used, so commenting out */
// private collapseEmptyTags(xmlData: string): string {
// const regEx = /<(([^ <>]+)[^<>]*)><\/\2>/g;
// const collapsed = xmlData.replace(regEx, "<$1/>");
// return collapsed;
// }
}

View File

@ -46,4 +46,24 @@ describe("Packer", () => {
});
});
});
describe("#toBase64String()", () => {
it("should create a standard docx file", async function() {
this.timeout(99999999);
const str = await packer.toBase64String(file);
assert.isDefined(str);
assert.isTrue(str.length > 0);
});
it("should handle exception if it throws any", () => {
// tslint:disable-next-line:no-any
const compiler = stub((packer as any).compiler, "compile");
compiler.throwsException();
return packer.toBase64String(file).catch((error) => {
assert.isDefined(error);
});
});
});
});

View File

@ -4,12 +4,12 @@ import { Compiler } from "./next-compiler";
export class Packer {
private readonly compiler: Compiler;
constructor() {
this.compiler = new Compiler();
constructor(prettifyXml?: boolean) {
this.compiler = new Compiler(prettifyXml);
}
public async toBuffer(file: File): Promise<Buffer> {
const zip = await this.compiler.compile(file);
const zip = this.compiler.compile(file);
const zipData = (await zip.generateAsync({
type: "nodebuffer",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
@ -19,7 +19,7 @@ export class Packer {
}
public async toBase64String(file: File): Promise<string> {
const zip = await this.compiler.compile(file);
const zip = this.compiler.compile(file);
const zipData = (await zip.generateAsync({
type: "base64",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
@ -29,7 +29,7 @@ export class Packer {
}
public async toBlob(file: File): Promise<Blob> {
const zip = await this.compiler.compile(file);
const zip = this.compiler.compile(file);
const zipData = (await zip.generateAsync({
type: "blob",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",

View File

@ -20,64 +20,70 @@ describe("ContentTypes", () => {
expect(tree["Types"]).to.be.an.instanceof(Array);
expect(tree["Types"][0]).to.deep.equal({ _attr: { xmlns: "http://schemas.openxmlformats.org/package/2006/content-types" } });
expect(tree["Types"][1]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/png", Extension: "png" } }] });
expect(tree["Types"][2]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/jpeg", Extension: "jpeg" } }] });
expect(tree["Types"][3]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/jpeg", Extension: "jpg" } }] });
expect(tree["Types"][4]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/bmp", Extension: "bmp" } }] });
expect(tree["Types"][5]).to.deep.equal({ Default: [{ _attr: { ContentType: "image/gif", Extension: "gif" } }] });
expect(tree["Types"][1]).to.deep.equal({ Default: { _attr: { ContentType: "image/png", Extension: "png" } } });
expect(tree["Types"][2]).to.deep.equal({ Default: { _attr: { ContentType: "image/jpeg", Extension: "jpeg" } } });
expect(tree["Types"][3]).to.deep.equal({ Default: { _attr: { ContentType: "image/jpeg", Extension: "jpg" } } });
expect(tree["Types"][4]).to.deep.equal({ Default: { _attr: { ContentType: "image/bmp", Extension: "bmp" } } });
expect(tree["Types"][5]).to.deep.equal({ Default: { _attr: { ContentType: "image/gif", Extension: "gif" } } });
expect(tree["Types"][6]).to.deep.equal({
Default: [{ _attr: { ContentType: "application/vnd.openxmlformats-package.relationships+xml", Extension: "rels" } }],
Default: { _attr: { ContentType: "application/vnd.openxmlformats-package.relationships+xml", Extension: "rels" } },
});
expect(tree["Types"][7]).to.deep.equal({ Default: [{ _attr: { ContentType: "application/xml", Extension: "xml" } }] });
expect(tree["Types"][7]).to.deep.equal({ Default: { _attr: { ContentType: "application/xml", Extension: "xml" } } });
expect(tree["Types"][8]).to.deep.equal({
Override: [
{
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
PartName: "/word/document.xml",
},
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
PartName: "/word/document.xml",
},
],
},
});
expect(tree["Types"][9]).to.deep.equal({
Override: [
{
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
PartName: "/word/styles.xml",
},
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
PartName: "/word/styles.xml",
},
],
},
});
expect(tree["Types"][10]).to.deep.equal({
Override: [
{
_attr: {
ContentType: "application/vnd.openxmlformats-package.core-properties+xml",
PartName: "/docProps/core.xml",
},
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-package.core-properties+xml",
PartName: "/docProps/core.xml",
},
],
},
});
expect(tree["Types"][11]).to.deep.equal({
Override: [
{
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.extended-properties+xml",
PartName: "/docProps/app.xml",
},
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.extended-properties+xml",
PartName: "/docProps/app.xml",
},
],
},
});
expect(tree["Types"][12]).to.deep.equal({
Override: [
{
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
PartName: "/word/numbering.xml",
},
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
PartName: "/word/numbering.xml",
},
],
},
});
expect(tree["Types"][13]).to.deep.equal({
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml",
PartName: "/word/footnotes.xml",
},
},
});
expect(tree["Types"][14]).to.deep.equal({
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
PartName: "/word/settings.xml",
},
},
});
});
});
@ -88,26 +94,22 @@ describe("ContentTypes", () => {
contentTypes.addFooter(102);
const tree = new Formatter().format(contentTypes);
expect(tree["Types"][14]).to.deep.equal({
Override: [
{
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
PartName: "/word/footer101.xml",
},
expect(tree["Types"][15]).to.deep.equal({
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
PartName: "/word/footer101.xml",
},
],
},
});
expect(tree["Types"][15]).to.deep.equal({
Override: [
{
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
PartName: "/word/footer102.xml",
},
expect(tree["Types"][16]).to.deep.equal({
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
PartName: "/word/footer102.xml",
},
],
},
});
});
});
@ -118,26 +120,22 @@ describe("ContentTypes", () => {
contentTypes.addHeader(202);
const tree = new Formatter().format(contentTypes);
expect(tree["Types"][14]).to.deep.equal({
Override: [
{
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
PartName: "/word/header201.xml",
},
expect(tree["Types"][15]).to.deep.equal({
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
PartName: "/word/header201.xml",
},
],
},
});
expect(tree["Types"][15]).to.deep.equal({
Override: [
{
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
PartName: "/word/header202.xml",
},
expect(tree["Types"][16]).to.deep.equal({
Override: {
_attr: {
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
PartName: "/word/header202.xml",
},
],
},
});
});
});

View File

@ -30,6 +30,7 @@ export class ContentTypes extends XmlComponent {
this.root.push(new Override("application/vnd.openxmlformats-officedocument.extended-properties+xml", "/docProps/app.xml"));
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", "/word/numbering.xml"));
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", "/word/footnotes.xml"));
this.root.push(new Override("application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", "/word/settings.xml"));
}
public addFooter(index: number): void {

View File

@ -17,7 +17,7 @@ describe("Body", () => {
expect(formatted)
.to.have.property("w:sectPr")
.and.to.be.an.instanceof(Array);
expect(formatted["w:sectPr"]).to.have.length(5);
expect(formatted["w:sectPr"]).to.have.length(4);
});
});
@ -30,14 +30,14 @@ describe("Body", () => {
const formatted = new Formatter().format(body)["w:body"];
expect(formatted).to.be.an.instanceof(Array);
const defaultSectionPr = formatted[0]["w:p"][1]["w:pPr"][0]["w:sectPr"];
const defaultSectionPr = formatted[0]["w:p"][0]["w:pPr"][0]["w:sectPr"];
// check that this is the default section and added first in paragraph
expect(defaultSectionPr[0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
expect(defaultSectionPr[0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
// check for new section (since it's the last one, it's direct child of body)
const newSection = formatted[1]["w:sectPr"];
expect(newSection[0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 10000, "w:w": 10000, "w:orient": "portrait" } }] });
expect(newSection[0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 10000, "w:w": 10000, "w:orient": "portrait" } } });
});
it("should add section with default parameters", () => {
@ -52,31 +52,27 @@ describe("Body", () => {
"w:body": [
{
"w:p": [
{ "w:pPr": [] },
{
"w:pPr": [
{
"w:sectPr": [
{ "w:pgSz": [{ _attr: { "w:w": 11906, "w:h": 16838, "w:orient": "portrait" } }] },
{ "w:pgSz": { _attr: { "w:w": 11906, "w:h": 16838, "w:orient": "portrait" } } },
{
"w:pgMar": [
{
_attr: {
"w:top": 1440,
"w:right": 1440,
"w:bottom": 1440,
"w:left": 1440,
"w:header": 708,
"w:footer": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
"w:pgMar": {
_attr: {
"w:top": 1440,
"w:right": 1440,
"w:bottom": 1440,
"w:left": 1440,
"w:header": 708,
"w:footer": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
],
},
},
{ "w:cols": [{ _attr: { "w:space": 708 } }] },
{ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] },
{ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] },
{ "w:cols": { _attr: { "w:space": 708, "w:num": 1 } } },
{ "w:docGrid": { _attr: { "w:linePitch": 360 } } },
],
},
],
@ -85,26 +81,23 @@ describe("Body", () => {
},
{
"w:sectPr": [
{ "w:pgSz": [{ _attr: { "w:w": 10000, "w:h": 10000, "w:orient": "portrait" } }] },
{ "w:pgSz": { _attr: { "w:w": 10000, "w:h": 10000, "w:orient": "portrait" } } },
{
"w:pgMar": [
{
_attr: {
"w:top": 1440,
"w:right": 1440,
"w:bottom": 1440,
"w:left": 1440,
"w:header": 708,
"w:footer": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
"w:pgMar": {
_attr: {
"w:top": 1440,
"w:right": 1440,
"w:bottom": 1440,
"w:left": 1440,
"w:header": 708,
"w:footer": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
],
},
},
{ "w:cols": [{ _attr: { "w:space": 708 } }] },
{ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] },
{ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] },
{ "w:cols": { _attr: { "w:space": 708, "w:num": 1 } } },
{ "w:docGrid": { _attr: { "w:linePitch": 360 } } },
],
},
],

View File

@ -37,7 +37,7 @@ export class Body extends XmlComponent {
}
public prepForXml(): IXmlableObject | undefined {
if (this.sections.length === 1) {
this.root.push(this.sections[0]);
this.root.push(this.sections.pop() as SectionProperties);
}
return super.prepForXml();

View File

@ -2,10 +2,12 @@ import { XmlAttributeComponent } from "file/xml-components";
export interface IColumnsAttributes {
readonly space?: number;
readonly num?: number;
}
export class ColumnsAttributes extends XmlAttributeComponent<IColumnsAttributes> {
protected readonly xmlKeys = {
space: "w:space",
num: "w:num",
};
}

View File

@ -2,11 +2,12 @@ import { XmlComponent } from "file/xml-components";
import { ColumnsAttributes } from "./columns-attributes";
export class Columns extends XmlComponent {
constructor(space: number) {
constructor(space: number, num: number) {
super("w:cols");
this.root.push(
new ColumnsAttributes({
space: space,
num: num,
}),
);
}

View File

@ -4,3 +4,4 @@ export * from "./header-reference";
export * from "./page-size";
export * from "./page-number";
export * from "./page-border";
export * from "./line-number";

View File

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

View File

@ -0,0 +1,38 @@
// http://officeopenxml.com/WPsectionLineNumbering.php
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export enum LineNumberRestartFormat {
CONTINUOUS = "continuous",
NEW_SECTION = "newSection",
NEW_PAGE = "newPage",
}
export interface ILineNumberAttributes {
readonly lineNumberCountBy?: number;
readonly lineNumberStart?: number;
readonly lineNumberRestart?: LineNumberRestartFormat;
readonly lineNumberDistance?: number;
}
export class LineNumberAttributes extends XmlAttributeComponent<ILineNumberAttributes> {
protected readonly xmlKeys = {
lineNumberCountBy: "w:countBy",
lineNumberStart: "w:start",
lineNumberRestart: "w:restart",
lineNumberDistance: "w:distance",
};
}
export class LineNumberType extends XmlComponent {
constructor(countBy?: number, start?: number, restart?: LineNumberRestartFormat, dist?: number) {
super("w:lnNumType");
this.root.push(
new LineNumberAttributes({
lineNumberCountBy: countBy,
lineNumberStart: start,
lineNumberRestart: restart,
lineNumberDistance: dist,
}),
);
}
}

View File

@ -21,8 +21,7 @@ describe("PageBorders", () => {
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:pgBorders"]);
expect(tree["w:pgBorders"]).to.be.an.instanceof(Array);
expect(tree["w:pgBorders"][0]).to.deep.equal({ _attr: { "w:display": "firstPage" } });
expect(tree["w:pgBorders"]).to.deep.equal({ _attr: { "w:display": "firstPage" } });
});
it("should create page borders with full configuration", () => {
@ -58,32 +57,24 @@ describe("PageBorders", () => {
expect(tree["w:pgBorders"]).to.be.an.instanceof(Array);
expect(tree["w:pgBorders"][0]).to.deep.equal({ _attr: { "w:display": "firstPage", "w:zOrder": "back" } });
expect(tree["w:pgBorders"][1]).to.deep.equal({
"w:top": [
{
_attr: { "w:color": "001122", "w:size": 10, "w:val": "doubleWave" },
},
],
"w:top": {
_attr: { "w:color": "001122", "w:size": 10, "w:val": "doubleWave" },
},
});
expect(tree["w:pgBorders"][2]).to.deep.equal({
"w:right": [
{
_attr: { "w:color": "223344", "w:size": 20, "w:val": "double" },
},
],
"w:right": {
_attr: { "w:color": "223344", "w:size": 20, "w:val": "double" },
},
});
expect(tree["w:pgBorders"][3]).to.deep.equal({
"w:bottom": [
{
_attr: { "w:color": "556677", "w:size": 30, "w:val": "single" },
},
],
"w:bottom": {
_attr: { "w:color": "556677", "w:size": 30, "w:val": "single" },
},
});
expect(tree["w:pgBorders"][4]).to.deep.equal({
"w:left": [
{
_attr: { "w:color": "889900", "w:size": 40, "w:val": "dotted" },
},
],
"w:left": {
_attr: { "w:color": "889900", "w:size": 40, "w:val": "dotted" },
},
});
});
});

View File

@ -1,6 +1,6 @@
// http://officeopenxml.com/WPsectionBorders.php
import { BorderStyle } from "file/styles";
import { IXmlableObject, XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { IgnoreIfEmptyXmlComponent, XmlAttributeComponent, XmlComponent } from "file/xml-components";
export enum PageBorderDisplay {
ALL_PAGES = "allPages",
@ -64,7 +64,7 @@ class PageBordersAttributes extends XmlAttributeComponent<IPageBorderAttributes>
};
}
export class PageBorders extends XmlComponent {
export class PageBorders extends IgnoreIfEmptyXmlComponent {
constructor(options?: IPageBordersOptions) {
super("w:pgBorders");
@ -97,10 +97,4 @@ export class PageBorders extends XmlComponent {
this.root.push(new PageBorder("w:left", options.pageBorderLeft));
}
}
public prepForXml(): IXmlableObject | undefined {
if (this.root.length > 0) {
return super.prepForXml();
}
}
}

View File

@ -12,8 +12,7 @@ describe("PageSize", () => {
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
expect(tree["w:pgSz"]).to.be.an.instanceof(Array);
expect(tree["w:pgSz"][0]).to.deep.equal({ _attr: { "w:h": 200, "w:w": 100, "w:orient": "portrait" } });
expect(tree["w:pgSz"]).to.deep.equal({ _attr: { "w:h": 200, "w:w": 100, "w:orient": "portrait" } });
});
it("should create page size with horizontal and invert the lengths", () => {
@ -21,8 +20,7 @@ describe("PageSize", () => {
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:pgSz"]);
expect(tree["w:pgSz"]).to.be.an.instanceof(Array);
expect(tree["w:pgSz"][0]).to.deep.equal({ _attr: { "w:h": 100, "w:w": 200, "w:orient": "landscape" } });
expect(tree["w:pgSz"]).to.deep.equal({ _attr: { "w:h": 100, "w:w": 200, "w:orient": "landscape" } });
});
});
});

View File

@ -25,7 +25,10 @@ describe("SectionProperties", () => {
footer: 708,
gutter: 0,
mirror: false,
space: 708,
column: {
space: 708,
count: 1,
},
linePitch: 360,
headers: {
default: new HeaderWrapper(media, 100),
@ -39,29 +42,27 @@ describe("SectionProperties", () => {
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
expect(tree["w:sectPr"][1]).to.deep.equal({
"w:pgMar": [
{
_attr: {
"w:bottom": 1440,
"w:footer": 708,
"w:top": 1440,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
"w:pgMar": {
_attr: {
"w:bottom": 1440,
"w:footer": 708,
"w:top": 1440,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
],
},
});
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": [{ _attr: { "w:space": 708 } }] });
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] });
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:headerReference": [{ _attr: { "r:id": "rId100", "w:type": "default" } }] });
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": [{ _attr: { "r:id": "rId200", "w:type": "even" } }] });
expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "cardinalText", "w:start": 10 } }] });
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": { _attr: { "w:space": 708, "w:num": 1 } } });
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": { _attr: { "w:linePitch": 360 } } });
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:headerReference": { _attr: { "r:id": "rId100", "w:type": "default" } } });
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": { _attr: { "r:id": "rId200", "w:type": "even" } } });
expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": { _attr: { "w:fmt": "cardinalText", "w:start": 10 } } });
});
it("should create section properties with no options", () => {
@ -69,26 +70,23 @@ describe("SectionProperties", () => {
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
expect(tree["w:sectPr"][1]).to.deep.equal({
"w:pgMar": [
{
_attr: {
"w:bottom": 1440,
"w:footer": 708,
"w:top": 1440,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
"w:pgMar": {
_attr: {
"w:bottom": 1440,
"w:footer": 708,
"w:top": 1440,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
],
},
});
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": [{ _attr: { "w:space": 708 } }] });
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] });
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] });
expect(tree["w:sectPr"][2]).to.deep.equal({ "w:cols": { _attr: { "w:space": 708, "w:num": 1 } } });
expect(tree["w:sectPr"][3]).to.deep.equal({ "w:docGrid": { _attr: { "w:linePitch": 360 } } });
});
it("should create section properties with changed options", () => {
@ -98,22 +96,20 @@ describe("SectionProperties", () => {
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
expect(tree["w:sectPr"][1]).to.deep.equal({
"w:pgMar": [
{
_attr: {
"w:bottom": 1440,
"w:footer": 708,
"w:top": 0,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
"w:pgMar": {
_attr: {
"w:bottom": 1440,
"w:footer": 708,
"w:top": 0,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
],
},
});
});
@ -124,22 +120,20 @@ describe("SectionProperties", () => {
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } }] });
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 16838, "w:w": 11906, "w:orient": "portrait" } } });
expect(tree["w:sectPr"][1]).to.deep.equal({
"w:pgMar": [
{
_attr: {
"w:bottom": 0,
"w:footer": 708,
"w:top": 1440,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
"w:pgMar": {
_attr: {
"w:bottom": 0,
"w:footer": 708,
"w:top": 1440,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
],
},
});
});
@ -151,22 +145,20 @@ describe("SectionProperties", () => {
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": [{ _attr: { "w:h": 0, "w:w": 0, "w:orient": "portrait" } }] });
expect(tree["w:sectPr"][0]).to.deep.equal({ "w:pgSz": { _attr: { "w:h": 0, "w:w": 0, "w:orient": "portrait" } } });
expect(tree["w:sectPr"][1]).to.deep.equal({
"w:pgMar": [
{
_attr: {
"w:bottom": 1440,
"w:footer": 708,
"w:top": 1440,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
"w:pgMar": {
_attr: {
"w:bottom": 1440,
"w:footer": 708,
"w:top": 1440,
"w:right": 1440,
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
],
},
});
});
@ -180,8 +172,28 @@ describe("SectionProperties", () => {
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
const pgBorders = tree["w:sectPr"].find((item) => item["w:pgBorders"] !== undefined);
expect(pgBorders).to.deep.equal({
"w:pgBorders": [{ _attr: { "w:offsetFrom": "page" } }],
"w:pgBorders": { _attr: { "w:offsetFrom": "page" } },
});
});
it("should create section properties with page number type, but without start attribute", () => {
const properties = new SectionProperties({
pageNumberFormatType: PageNumberFormat.UPPER_ROMAN,
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
const pgNumType = tree["w:sectPr"].find((item) => item["w:pgNumType"] !== undefined);
expect(pgNumType).to.deep.equal({
"w:pgNumType": { _attr: { "w:fmt": "upperRoman" } },
});
});
it("should create section properties without page number type", () => {
const properties = new SectionProperties({});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
const pgNumType = tree["w:sectPr"].find((item) => item["w:pgNumType"] !== undefined);
expect(pgNumType).to.equal(undefined);
});
});
});

View File

@ -4,17 +4,17 @@ import { HeaderWrapper } from "file/header-wrapper";
import { XmlComponent } from "file/xml-components";
import { Columns } from "./columns/columns";
import { IColumnsAttributes } from "./columns/columns-attributes";
import { DocumentGrid } from "./doc-grid/doc-grid";
import { IDocGridAttributesProperties } from "./doc-grid/doc-grid-attributes";
import { FooterReferenceType } from "./footer-reference";
import { FooterReference } from "./footer-reference/footer-reference";
import { HeaderReferenceType } from "./header-reference";
import { HeaderReference } from "./header-reference/header-reference";
import { ILineNumberAttributes, LineNumberType } from "./line-number";
import { IPageBordersOptions, PageBorders } from "./page-border";
import { PageMargin } from "./page-margin/page-margin";
import { IPageMarginAttributes } from "./page-margin/page-margin-attributes";
import { IPageNumberTypeAttributes, PageNumberFormat, PageNumberType } from "./page-number";
import { IPageNumberTypeAttributes, PageNumberType } from "./page-number";
import { PageSize } from "./page-size/page-size";
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
import { TitlePage } from "./title-page/title-page";
@ -39,18 +39,24 @@ interface ITitlePageOptions {
export type SectionPropertiesOptions = IPageSizeAttributes &
IPageMarginAttributes &
IColumnsAttributes &
IDocGridAttributesProperties &
IHeadersOptions &
IFootersOptions &
IPageNumberTypeAttributes &
ILineNumberAttributes &
IPageBordersOptions &
ITitlePageOptions;
ITitlePageOptions & {
readonly column?: {
readonly space?: number;
readonly count?: number;
};
};
// Need to decouple this from the attributes
export class SectionProperties extends XmlComponent {
private readonly options: SectionPropertiesOptions;
constructor(options: SectionPropertiesOptions = {}) {
constructor(options: SectionPropertiesOptions = { column: {} }) {
super("w:sectPr");
const {
@ -64,13 +70,17 @@ export class SectionProperties extends XmlComponent {
footer = 708,
gutter = 0,
mirror = false,
space = 708,
column = {},
linePitch = 360,
orientation = PageOrientation.PORTRAIT,
headers,
footers,
pageNumberFormatType = PageNumberFormat.DECIMAL,
pageNumberFormatType,
pageNumberStart,
lineNumberCountBy,
lineNumberStart,
lineNumberRestart,
lineNumberDistance,
pageBorders,
pageBorderTop,
pageBorderRight,
@ -82,13 +92,19 @@ export class SectionProperties extends XmlComponent {
this.options = options;
this.root.push(new PageSize(width, height, orientation));
this.root.push(new PageMargin(top, right, bottom, left, header, footer, gutter, mirror));
this.root.push(new Columns(space));
this.root.push(new Columns(column.space ? column.space : 708, column.count ? column.count : 1));
this.root.push(new DocumentGrid(linePitch));
this.addHeaders(headers);
this.addFooters(footers);
this.root.push(new PageNumberType(pageNumberStart, pageNumberFormatType));
if (pageNumberStart || pageNumberFormatType) {
this.root.push(new PageNumberType(pageNumberStart, pageNumberFormatType));
}
if (lineNumberCountBy || lineNumberStart || lineNumberRestart || lineNumberDistance) {
this.root.push(new LineNumberType(lineNumberCountBy, lineNumberStart, lineNumberRestart, lineNumberDistance));
}
if (pageBorders || pageBorderTop || pageBorderRight || pageBorderBottom || pageBorderLeft) {
this.root.push(

View File

@ -11,8 +11,7 @@ describe("PageSize", () => {
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" } });
expect(tree["w:titlePg"]).to.deep.equal({ _attr: { "w:val": "1" } });
});
});
});

View File

@ -52,14 +52,17 @@ describe("Document", () => {
expect(body[0])
.to.have.property("w:p")
.which.includes({
"w:r": [{ "w:rPr": [] }, { "w:t": [{ _attr: { "xml:space": "preserve" } }, "sample paragraph text"] }],
"w:r": [{ "w:t": [{ _attr: { "xml:space": "preserve" } }, "sample paragraph text"] }],
});
});
});
describe("#createTable", () => {
it("should create a new table and append it to body", () => {
const table = document.createTable(2, 3);
const table = document.createTable({
rows: 2,
columns: 3,
});
expect(table).to.be.an.instanceof(Table);
const body = new Formatter().format(document)["w:document"][1]["w:body"];
expect(body)
@ -69,7 +72,10 @@ describe("Document", () => {
});
it("should create a table with the correct dimensions", () => {
document.createTable(2, 3);
document.createTable({
rows: 2,
columns: 3,
});
const body = new Formatter().format(document)["w:document"][1]["w:body"];
expect(body)
.to.be.an("array")
@ -78,9 +84,9 @@ describe("Document", () => {
.to.have.property("w:tbl")
.which.includes({
"w:tblGrid": [
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
{ "w:gridCol": [{ _attr: { "w:w": 1 } }] },
{ "w:gridCol": { _attr: { "w:w": 100 } } },
{ "w:gridCol": { _attr: { "w:w": 100 } } },
{ "w:gridCol": { _attr: { "w:w": 100 } } },
],
});
expect(body[0]["w:tbl"].filter((x) => x["w:tr"])).to.have.length(2);

View File

@ -1,7 +1,7 @@
// http://officeopenxml.com/WPdocument.php
import { XmlComponent } from "file/xml-components";
import { Paragraph } from "../paragraph";
import { Table } from "../table";
import { ITableOptions, Table } from "../table";
import { TableOfContents } from "../table-of-contents";
import { Body } from "./body";
import { SectionPropertiesOptions } from "./body/section-properties";
@ -53,12 +53,13 @@ export class Document extends XmlComponent {
return para;
}
public addTable(table: Table): void {
public addTable(table: Table): Document {
this.body.push(table);
return this;
}
public createTable(rows: number, cols: number): Table {
const table = new Table(rows, cols);
public createTable(options: ITableOptions): Table {
const table = new Table(options);
this.addTable(table);
return table;
}

View File

@ -8,7 +8,20 @@ import { Anchor } from "./anchor";
function createAnchor(drawingOptions: IDrawingOptions): Anchor {
return new Anchor(
1,
{
fileName: "test.png",
stream: new Buffer(""),
dimensions: {
pixels: {
x: 0,
y: 0,
},
emus: {
x: 0,
y: 0,
},
},
},
{
pixels: {
x: 100,

View File

@ -1,5 +1,5 @@
// http://officeopenxml.com/drwPicFloating.php
import { IMediaDataDimensions } from "file/media";
import { IMediaData, IMediaDataDimensions } from "file/media";
import { XmlComponent } from "file/xml-components";
import { IDrawingOptions } from "../drawing";
import { HorizontalPosition, IFloating, SimplePos, VerticalPosition } from "../floating";
@ -21,7 +21,7 @@ const defaultOptions: IFloating = {
};
export class Anchor extends XmlComponent {
constructor(referenceId: number, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) {
constructor(mediaData: IMediaData, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) {
super("wp:anchor");
const floating = {
@ -77,6 +77,6 @@ export class Anchor extends XmlComponent {
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(new Graphic(mediaData, dimensions.emus.x, dimensions.emus.y));
}
}

View File

@ -1,6 +1,6 @@
import { assert } from "chai";
import { expect } from "chai";
import { Utility } from "tests/utility";
import { Formatter } from "export/formatter";
import { Drawing, IDrawingOptions } from "./drawing";
@ -11,7 +11,6 @@ function createDrawing(drawingOptions?: IDrawingOptions): Drawing {
return new Drawing(
{
fileName: "test.jpg",
referenceId: 1,
stream: Buffer.from(imageBase64Data, "base64"),
path: path,
dimensions: {
@ -33,16 +32,181 @@ describe("Drawing", () => {
let currentBreak: Drawing;
describe("#constructor()", () => {
it("should create a Drawing with correct root key", () => {
currentBreak = createDrawing();
const newJson = Utility.jsonify(currentBreak);
assert.equal(newJson.rootKey, "w:drawing");
});
it("should create a drawing with inline element when there are no options passed", () => {
currentBreak = createDrawing();
const newJson = Utility.jsonify(currentBreak);
assert.equal(newJson.root[0].rootKey, "wp:inline");
const tree = new Formatter().format(currentBreak);
expect(tree).to.deep.equal({
"w:drawing": [
{
"wp:inline": [
{
_attr: {
distB: 0,
distL: 0,
distR: 0,
distT: 0,
},
},
{
"wp:extent": {
_attr: {
cx: 952500,
cy: 952500,
},
},
},
{
"wp:effectExtent": {
_attr: {
b: 0,
l: 0,
r: 0,
t: 0,
},
},
},
{
"wp:docPr": {
_attr: {
descr: "",
id: 0,
name: "",
},
},
},
{
"wp:cNvGraphicFramePr": [
{
"a:graphicFrameLocks": {
_attr: {
// tslint:disable-next-line:object-literal-key-quotes
noChangeAspect: 1,
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
},
},
},
],
},
{
"a:graphic": [
{
_attr: {
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
},
},
{
"a:graphicData": [
{
_attr: {
uri: "http://schemas.openxmlformats.org/drawingml/2006/picture",
},
},
{
"pic:pic": [
{
_attr: {
"xmlns:pic": "http://schemas.openxmlformats.org/drawingml/2006/picture",
},
},
{
"pic:nvPicPr": [
{
"pic:cNvPr": {
_attr: {
desc: "",
id: 0,
name: "",
},
},
},
{
"pic:cNvPicPr": [
{
"a:picLocks": {
_attr: {
noChangeArrowheads: 1,
noChangeAspect: 1,
},
},
},
],
},
],
},
{
"pic:blipFill": [
{
"a:blip": {
_attr: {
// tslint:disable-next-line:object-literal-key-quotes
cstate: "none",
"r:embed": "rId{test.jpg}",
},
},
},
{
"a:srcRect": {},
},
{
"a:stretch": [
{
"a:fillRect": {},
},
],
},
],
},
{
"pic:spPr": [
{
_attr: {
bwMode: "auto",
},
},
{
"a:xfrm": [
{
"a:ext": {
_attr: {
cx: 952500,
cy: 952500,
},
},
},
{
"a:off": {
_attr: {
x: 0,
y: 0,
},
},
},
],
},
{
"a:prstGeom": [
{
_attr: {
prst: "rect",
},
},
{
"a:avLst": {},
},
],
},
],
},
],
},
],
},
],
},
],
},
],
});
});
it("should create a drawing with anchor element when there options are passed", () => {
@ -56,8 +220,220 @@ describe("Drawing", () => {
},
},
});
const newJson = Utility.jsonify(currentBreak);
assert.equal(newJson.root[0].rootKey, "wp:anchor");
const tree = new Formatter().format(currentBreak);
expect(tree).to.deep.equal({
"w:drawing": [
{
"wp:anchor": [
{
_attr: {
allowOverlap: "1",
behindDoc: "0",
distB: 0,
distL: 0,
distR: 0,
distT: 0,
layoutInCell: "1",
locked: "0",
relativeHeight: 952500,
simplePos: "0",
},
},
{
"wp:simplePos": {
_attr: {
x: 0,
y: 0,
},
},
},
{
"wp:positionH": [
{
_attr: {
relativeFrom: "page",
},
},
{
"wp:posOffset": ["0"],
},
],
},
{
"wp:positionV": [
{
_attr: {
relativeFrom: "page",
},
},
{
"wp:posOffset": ["0"],
},
],
},
{
"wp:extent": {
_attr: {
cx: 952500,
cy: 952500,
},
},
},
{
"wp:effectExtent": {
_attr: {
b: 0,
l: 0,
r: 0,
t: 0,
},
},
},
{
"wp:wrapNone": {},
},
{
"wp:docPr": {
_attr: {
descr: "",
id: 0,
name: "",
},
},
},
{
"wp:cNvGraphicFramePr": [
{
"a:graphicFrameLocks": {
_attr: {
// tslint:disable-next-line:object-literal-key-quotes
noChangeAspect: 1,
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
},
},
},
],
},
{
"a:graphic": [
{
_attr: {
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
},
},
{
"a:graphicData": [
{
_attr: {
uri: "http://schemas.openxmlformats.org/drawingml/2006/picture",
},
},
{
"pic:pic": [
{
_attr: {
"xmlns:pic": "http://schemas.openxmlformats.org/drawingml/2006/picture",
},
},
{
"pic:nvPicPr": [
{
"pic:cNvPr": {
_attr: {
desc: "",
id: 0,
name: "",
},
},
},
{
"pic:cNvPicPr": [
{
"a:picLocks": {
_attr: {
noChangeArrowheads: 1,
noChangeAspect: 1,
},
},
},
],
},
],
},
{
"pic:blipFill": [
{
"a:blip": {
_attr: {
// tslint:disable-next-line:object-literal-key-quotes
cstate: "none",
"r:embed": "rId{test.jpg}",
},
},
},
{
"a:srcRect": {},
},
{
"a:stretch": [
{
"a:fillRect": {},
},
],
},
],
},
{
"pic:spPr": [
{
_attr: {
bwMode: "auto",
},
},
{
"a:xfrm": [
{
"a:ext": {
_attr: {
cx: 952500,
cy: 952500,
},
},
},
{
"a:off": {
_attr: {
x: 0,
y: 0,
},
},
},
],
},
{
"a:prstGeom": [
{
_attr: {
prst: "rect",
},
},
{
"a:avLst": {},
},
],
},
],
},
],
},
],
},
],
},
],
},
],
});
});
});
});

View File

@ -21,15 +21,11 @@ export class Drawing extends XmlComponent {
constructor(imageData: IMediaData, drawingOptions: IDrawingOptions = {}) {
super("w:drawing");
if (imageData === undefined) {
throw new Error("imageData cannot be undefined");
}
if (!drawingOptions.floating) {
this.inline = new Inline(imageData.referenceId, imageData.dimensions);
this.inline = new Inline(imageData, imageData.dimensions);
this.root.push(this.inline);
} else {
this.root.push(new Anchor(imageData.referenceId, imageData.dimensions, drawingOptions));
this.root.push(new Anchor(imageData, imageData.dimensions, drawingOptions));
}
}

View File

@ -1,6 +1,6 @@
import { assert } from "chai";
import { expect } from "chai";
import { Utility } from "tests/utility";
import { Formatter } from "export/formatter";
import { Align } from "./align";
import { VerticalPositionAlign } from "./floating-position";
@ -8,9 +8,10 @@ import { VerticalPositionAlign } from "./floating-position";
describe("Align", () => {
describe("#constructor()", () => {
it("should create a element with correct root key", () => {
const newJson = Utility.jsonify(new Align(VerticalPositionAlign.CENTER));
assert.equal(newJson.rootKey, "wp:align");
assert.include(newJson.root[0], VerticalPositionAlign.CENTER);
const tree = new Formatter().format(new Align(VerticalPositionAlign.CENTER));
expect(tree).to.deep.equal({
"wp:align": ["center"],
});
});
});
});

View File

@ -1,6 +1,6 @@
import { assert } from "chai";
import { expect } from "chai";
import { Utility } from "tests/utility";
import { Formatter } from "export/formatter";
import { HorizontalPositionAlign, HorizontalPositionRelativeFrom } from "./floating-position";
import { HorizontalPosition } from "./horizontal-position";
@ -8,35 +8,45 @@ import { HorizontalPosition } from "./horizontal-position";
describe("HorizontalPosition", () => {
describe("#constructor()", () => {
it("should create a element with position align", () => {
const newJson = Utility.jsonify(
const tree = new Formatter().format(
new HorizontalPosition({
relative: HorizontalPositionRelativeFrom.MARGIN,
align: HorizontalPositionAlign.CENTER,
}),
);
assert.equal(newJson.rootKey, "wp:positionH");
assert.include(newJson.root[0].root, {
relativeFrom: "margin",
expect(tree).to.deep.equal({
"wp:positionH": [
{
_attr: {
relativeFrom: "margin",
},
},
{
"wp:align": ["center"],
},
],
});
assert.equal(newJson.root[1].rootKey, "wp:align");
assert.include(newJson.root[1].root, "center");
});
it("should create a element with offset", () => {
const newJson = Utility.jsonify(
const tree = new Formatter().format(
new HorizontalPosition({
relative: HorizontalPositionRelativeFrom.MARGIN,
offset: 40,
}),
);
assert.equal(newJson.rootKey, "wp:positionH");
assert.include(newJson.root[0].root, {
relativeFrom: "margin",
expect(tree).to.deep.equal({
"wp:positionH": [
{
_attr: {
relativeFrom: "margin",
},
},
{
"wp:posOffset": ["40"],
},
],
});
assert.equal(newJson.root[1].rootKey, "wp:posOffset");
assert.include(newJson.root[1].root[0], 40);
});
});
});

View File

@ -1,15 +1,16 @@
import { assert } from "chai";
import { expect } from "chai";
import { Utility } from "tests/utility";
import { Formatter } from "export/formatter";
import { PositionOffset } from "./position-offset";
describe("PositionOffset", () => {
describe("#constructor()", () => {
it("should create a element with correct root key", () => {
const newJson = Utility.jsonify(new PositionOffset(50));
assert.equal(newJson.rootKey, "wp:posOffset");
assert.equal(newJson.root[0], 50);
const tree = new Formatter().format(new PositionOffset(50));
expect(tree).to.deep.equal({
"wp:posOffset": ["50"],
});
});
});
});

View File

@ -1,17 +1,20 @@
import { assert } from "chai";
import { expect } from "chai";
import { Utility } from "tests/utility";
import { Formatter } from "export/formatter";
import { SimplePos } from "./simple-pos";
describe("SimplePos", () => {
describe("#constructor()", () => {
it("should create a element with correct root key", () => {
const newJson = Utility.jsonify(new SimplePos());
assert.equal(newJson.rootKey, "wp:simplePos");
assert.include(newJson.root[0].root, {
x: 0,
y: 0,
const tree = new Formatter().format(new SimplePos());
expect(tree).to.deep.equal({
"wp:simplePos": {
_attr: {
x: 0,
y: 0,
},
},
});
});
});

View File

@ -1,6 +1,6 @@
import { assert } from "chai";
import { expect } from "chai";
import { Utility } from "tests/utility";
import { Formatter } from "export/formatter";
import { VerticalPositionAlign, VerticalPositionRelativeFrom } from "./floating-position";
import { VerticalPosition } from "./vertical-position";
@ -8,35 +8,45 @@ import { VerticalPosition } from "./vertical-position";
describe("VerticalPosition", () => {
describe("#constructor()", () => {
it("should create a element with position align", () => {
const newJson = Utility.jsonify(
const tree = new Formatter().format(
new VerticalPosition({
relative: VerticalPositionRelativeFrom.MARGIN,
align: VerticalPositionAlign.INSIDE,
}),
);
assert.equal(newJson.rootKey, "wp:positionV");
assert.include(newJson.root[0].root, {
relativeFrom: "margin",
expect(tree).to.deep.equal({
"wp:positionV": [
{
_attr: {
relativeFrom: "margin",
},
},
{
"wp:align": ["inside"],
},
],
});
assert.equal(newJson.root[1].rootKey, "wp:align");
assert.include(newJson.root[1].root, "inside");
});
it("should create a element with offset", () => {
const newJson = Utility.jsonify(
const tree = new Formatter().format(
new VerticalPosition({
relative: VerticalPositionRelativeFrom.MARGIN,
offset: 40,
}),
);
assert.equal(newJson.rootKey, "wp:positionV");
assert.include(newJson.root[0].root, {
relativeFrom: "margin",
expect(tree).to.deep.equal({
"wp:positionV": [
{
_attr: {
relativeFrom: "margin",
},
},
{
"wp:posOffset": ["40"],
},
],
});
assert.equal(newJson.root[1].rootKey, "wp:posOffset");
assert.include(newJson.root[1].root[0], 40);
});
});
});

View File

@ -1,11 +1,13 @@
import { IMediaData } from "file/media";
import { XmlComponent } from "file/xml-components";
import { GraphicDataAttributes } from "./graphic-data-attribute";
import { Pic } from "./pic";
export class GraphicData extends XmlComponent {
private readonly pic: Pic;
constructor(referenceId: number, x: number, y: number) {
constructor(mediaData: IMediaData, x: number, y: number) {
super("a:graphicData");
this.root.push(
@ -14,7 +16,7 @@ export class GraphicData extends XmlComponent {
}),
);
this.pic = new Pic(referenceId, x, y);
this.pic = new Pic(mediaData, x, y);
this.root.push(this.pic);
}

View File

@ -1,12 +1,15 @@
import { IMediaData } from "file/media";
import { XmlComponent } from "file/xml-components";
import { Blip } from "./blip";
import { SourceRectangle } from "./source-rectangle";
import { Stretch } from "./stretch";
export class BlipFill extends XmlComponent {
constructor(referenceId: number) {
constructor(mediaData: IMediaData) {
super("pic:blipFill");
this.root.push(new Blip(referenceId));
this.root.push(new Blip(mediaData));
this.root.push(new SourceRectangle());
this.root.push(new Stretch());
}

View File

@ -1,3 +1,4 @@
import { IMediaData } from "file/media";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
interface IBlipProperties {
@ -13,11 +14,11 @@ class BlipAttributes extends XmlAttributeComponent<IBlipProperties> {
}
export class Blip extends XmlComponent {
constructor(referenceId: number) {
constructor(mediaData: IMediaData) {
super("a:blip");
this.root.push(
new BlipAttributes({
embed: `rId${referenceId}`,
embed: `rId{${mediaData.fileName}}`,
cstate: "none",
}),
);

View File

@ -1,5 +1,7 @@
// http://officeopenxml.com/drwPic.php
import { IMediaData } from "file/media";
import { XmlComponent } from "file/xml-components";
import { BlipFill } from "./blip/blip-fill";
import { NonVisualPicProperties } from "./non-visual-pic-properties/non-visual-pic-properties";
import { PicAttributes } from "./pic-attributes";
@ -8,7 +10,7 @@ import { ShapeProperties } from "./shape-properties/shape-properties";
export class Pic extends XmlComponent {
private readonly shapeProperties: ShapeProperties;
constructor(referenceId: number, x: number, y: number) {
constructor(mediaData: IMediaData, x: number, y: number) {
super("pic:pic");
this.root.push(
@ -20,7 +22,7 @@ export class Pic extends XmlComponent {
this.shapeProperties = new ShapeProperties(x, y);
this.root.push(new NonVisualPicProperties());
this.root.push(new BlipFill(referenceId));
this.root.push(new BlipFill(mediaData));
this.root.push(new ShapeProperties(x, y));
}

View File

@ -1,4 +1,6 @@
import { IMediaData } from "file/media";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { GraphicData } from "./graphic-data";
interface IGraphicProperties {
@ -14,7 +16,7 @@ class GraphicAttributes extends XmlAttributeComponent<IGraphicProperties> {
export class Graphic extends XmlComponent {
private readonly data: GraphicData;
constructor(referenceId: number, x: number, y: number) {
constructor(mediaData: IMediaData, x: number, y: number) {
super("a:graphic");
this.root.push(
new GraphicAttributes({
@ -22,7 +24,7 @@ export class Graphic extends XmlComponent {
}),
);
this.data = new GraphicData(referenceId, x, y);
this.data = new GraphicData(mediaData, x, y);
this.root.push(this.data);
}

View File

@ -1,5 +1,5 @@
// http://officeopenxml.com/drwPicInline.php
import { IMediaDataDimensions } from "file/media";
import { IMediaData, IMediaDataDimensions } from "file/media";
import { XmlComponent } from "file/xml-components";
import { DocProperties } from "./../doc-properties/doc-properties";
import { EffectExtent } from "./../effect-extent/effect-extent";
@ -12,7 +12,7 @@ export class Inline extends XmlComponent {
private readonly extent: Extent;
private readonly graphic: Graphic;
constructor(referenceId: number, private readonly dimensions: IMediaDataDimensions) {
constructor(readonly mediaData: IMediaData, private readonly dimensions: IMediaDataDimensions) {
super("wp:inline");
this.root.push(
@ -25,7 +25,7 @@ export class Inline extends XmlComponent {
);
this.extent = new Extent(dimensions.emus.x, dimensions.emus.y);
this.graphic = new Graphic(referenceId, dimensions.emus.x, dimensions.emus.y);
this.graphic = new Graphic(mediaData, dimensions.emus.x, dimensions.emus.y);
this.root.push(this.extent);
this.root.push(new EffectExtent());

View File

@ -1,12 +1,15 @@
import { expect } from "chai";
import * as sinon from "sinon";
import { Formatter } from "export/formatter";
import { File } from "./file";
import { Paragraph } from "./paragraph";
import { Table } from "./table";
describe("File", () => {
describe("#constructor", () => {
it("should create with correct headers", () => {
it("should create with correct headers and footers", () => {
const doc = new File();
const header = doc.createHeader();
const footer = doc.createFooter();
@ -22,8 +25,28 @@ describe("File", () => {
const tree = new Formatter().format(doc.Document.Body);
expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"][0]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][1]["w:sectPr"][5]["w:footerReference"][0]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][1]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("default");
});
it("should create with first headers and footers", () => {
const doc = new File();
const header = doc.createHeader();
const footer = doc.createFooter();
doc.addSection({
headers: {
first: header,
},
footers: {
first: footer,
},
});
const tree = new Formatter().format(doc.Document.Body);
expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("first");
expect(tree["w:body"][1]["w:sectPr"][5]["w:footerReference"]._attr["w:type"]).to.equal("first");
});
it("should create with correct headers", () => {
@ -46,13 +69,105 @@ describe("File", () => {
const tree = new Formatter().format(doc.Document.Body);
expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"][0]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][1]["w:sectPr"][5]["w:headerReference"][0]._attr["w:type"]).to.equal("first");
expect(tree["w:body"][1]["w:sectPr"][6]["w:headerReference"][0]._attr["w:type"]).to.equal("even");
expect(tree["w:body"][1]["w:sectPr"][4]["w:headerReference"]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][1]["w:sectPr"][5]["w:headerReference"]._attr["w:type"]).to.equal("first");
expect(tree["w:body"][1]["w:sectPr"][6]["w:headerReference"]._attr["w:type"]).to.equal("even");
expect(tree["w:body"][1]["w:sectPr"][7]["w:footerReference"][0]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][1]["w:sectPr"][8]["w:footerReference"][0]._attr["w:type"]).to.equal("first");
expect(tree["w:body"][1]["w:sectPr"][9]["w:footerReference"][0]._attr["w:type"]).to.equal("even");
expect(tree["w:body"][1]["w:sectPr"][7]["w:footerReference"]._attr["w:type"]).to.equal("default");
expect(tree["w:body"][1]["w:sectPr"][8]["w:footerReference"]._attr["w:type"]).to.equal("first");
expect(tree["w:body"][1]["w:sectPr"][9]["w:footerReference"]._attr["w:type"]).to.equal("even");
});
});
describe("#addParagraph", () => {
it("should call the underlying document's addParagraph", () => {
const file = new File();
const spy = sinon.spy(file.Document, "addParagraph");
file.addParagraph(new Paragraph());
expect(spy.called).to.equal(true);
});
});
describe("#addTable", () => {
it("should call the underlying document's addTable", () => {
const wrapper = new File();
const spy = sinon.spy(wrapper.Document, "addTable");
wrapper.addTable(
new Table({
rows: 1,
columns: 1,
}),
);
expect(spy.called).to.equal(true);
});
});
describe("#createTable", () => {
it("should call the underlying document's createTable", () => {
const wrapper = new File();
const spy = sinon.spy(wrapper.Document, "createTable");
wrapper.createTable({
rows: 1,
columns: 1,
});
expect(spy.called).to.equal(true);
});
});
describe("#addTableOfContents", () => {
it("should call the underlying document's addTableOfContents", () => {
const wrapper = new File();
const spy = sinon.spy(wrapper.Document, "addTableOfContents");
// tslint:disable-next-line:no-any
wrapper.addTableOfContents({} as any);
expect(spy.called).to.equal(true);
});
});
describe("#createParagraph", () => {
it("should call the underlying document's createParagraph", () => {
const wrapper = new File();
const spy = sinon.spy(wrapper.Document, "createParagraph");
wrapper.createParagraph("test");
expect(spy.called).to.equal(true);
});
});
describe("#addImage", () => {
it("should call the underlying document's addImage", () => {
const wrapper = new File();
const spy = sinon.spy(wrapper.Document, "addParagraph");
// tslint:disable-next-line:no-any
wrapper.addImage({} as any);
expect(spy.called).to.equal(true);
});
});
describe("#createImage", () => {
it("should call the underlying document's createImage", () => {
const wrapper = new File();
const spy = sinon.spy(wrapper.Media, "addMedia");
const wrapperSpy = sinon.spy(wrapper.Document, "addParagraph");
wrapper.createImage("");
expect(spy.called).to.equal(true);
expect(wrapperSpy.called).to.equal(true);
});
});
describe("#createFootnote", () => {
it("should call the underlying document's createFootnote", () => {
const wrapper = new File();
const spy = sinon.spy(wrapper.FootNotes, "createFootNote");
wrapper.createFootnote(new Paragraph(""));
expect(spy.called).to.equal(true);
});
});
});

View File

@ -19,11 +19,12 @@ import { Image, Media } from "./media";
import { Numbering } from "./numbering";
import { Bookmark, Hyperlink, Paragraph } from "./paragraph";
import { Relationships } from "./relationships";
import { TargetModeType } from "./relationships/relationship/relationship";
import { Settings } from "./settings";
import { Styles } from "./styles";
import { ExternalStylesFactory } from "./styles/external-styles-factory";
import { DefaultStylesFactory } from "./styles/factory";
import { Table } from "./table";
import { ITableOptions, Table } from "./table";
import { TableOfContents } from "./table-of-contents";
export class File {
@ -57,12 +58,13 @@ export class File {
this.coreProperties = new CoreProperties(options);
this.numbering = new Numbering();
this.docRelationships = new Relationships();
this.media = new Media();
this.fileRelationships = new Relationships();
this.appProperties = new AppProperties();
this.footNotes = new FootNotes();
this.contentTypes = new ContentTypes();
this.media = fileProperties.template && fileProperties.template.media ? fileProperties.template.media : new Media();
if (fileProperties.template) {
this.currentRelationshipId = fileProperties.template.currentRelationshipId + 1;
}
@ -72,7 +74,8 @@ export class File {
throw Error("can not use both template and external styles");
}
if (fileProperties.template) {
this.styles = fileProperties.template.styles;
const stylesFactory = new ExternalStylesFactory();
this.styles = stylesFactory.newInstance(fileProperties.template.styles);
} else if (options.externalStyles) {
const stylesFactory = new ExternalStylesFactory();
this.styles = stylesFactory.newInstance(options.externalStyles);
@ -109,8 +112,9 @@ export class File {
this.settings = new Settings();
}
public addTableOfContents(toc: TableOfContents): void {
public addTableOfContents(toc: TableOfContents): File {
this.document.addTableOfContents(toc);
return this;
}
public addParagraph(paragraph: Paragraph): File {
@ -122,12 +126,13 @@ export class File {
return this.document.createParagraph(text);
}
public addTable(table: Table): void {
return this.document.addTable(table);
public addTable(table: Table): File {
this.document.addTable(table);
return this;
}
public createTable(rows: number, cols: number): Table {
return this.document.createTable(rows, cols);
public createTable(options: ITableOptions): Table {
return this.document.createTable(options);
}
public addImage(image: Image): File {
@ -154,7 +159,7 @@ export class File {
hyperlink.linkId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
link,
"External",
TargetModeType.EXTERNAL,
);
return hyperlink;
}
@ -319,6 +324,11 @@ export class File {
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
"footnotes.xml",
);
this.docRelationships.createRelationship(
this.currentRelationshipId++,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings",
"settings.xml",
);
}
private groupHeaders(headers: IDocumentHeader[], group: IHeaderFooterGroup<HeaderWrapper> = {}): IHeaderFooterGroup<HeaderWrapper> {
@ -326,12 +336,6 @@ export class File {
for (const header of headers) {
switch (header.type) {
case HeaderReferenceType.DEFAULT:
newGroup = {
...newGroup,
default: header.header,
};
break;
case HeaderReferenceType.FIRST:
newGroup = {
...newGroup,
@ -344,6 +348,7 @@ export class File {
even: header.header,
};
break;
case HeaderReferenceType.DEFAULT:
default:
newGroup = {
...newGroup,
@ -361,12 +366,6 @@ export class File {
for (const footer of footers) {
switch (footer.type) {
case FooterReferenceType.DEFAULT:
newGroup = {
...newGroup,
default: footer.footer,
};
break;
case FooterReferenceType.FIRST:
newGroup = {
...newGroup,
@ -379,6 +378,7 @@ export class File {
even: footer.footer,
};
break;
case FooterReferenceType.DEFAULT:
default:
newGroup = {
...newGroup,

View File

@ -0,0 +1,88 @@
import { expect } from "chai";
import * as sinon from "sinon";
import { FooterWrapper } from "./footer-wrapper";
import { Media } from "./media";
import { Paragraph } from "./paragraph";
import { Table } from "./table";
describe("FooterWrapper", () => {
describe("#addParagraph", () => {
it("should call the underlying footer's addParagraph", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.Footer, "addParagraph");
file.addParagraph(new Paragraph());
expect(spy.called).to.equal(true);
});
});
describe("#addTable", () => {
it("should call the underlying footer's addParagraph", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.Footer, "addTable");
file.addTable(
new Table({
rows: 1,
columns: 1,
}),
);
expect(spy.called).to.equal(true);
});
});
describe("#createTable", () => {
it("should call the underlying footer's createTable", () => {
const wrapper = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(wrapper.Footer, "createTable");
wrapper.createTable(1, 1);
expect(spy.called).to.equal(true);
});
});
describe("#createParagraph", () => {
it("should call the underlying footer's createParagraph", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.Footer, "addParagraph");
file.createParagraph();
expect(spy.called).to.equal(true);
});
});
describe("#addImage", () => {
it("should call the underlying footer's addImage", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.Footer, "addParagraph");
// tslint:disable-next-line:no-any
file.addImage({} as any);
expect(spy.called).to.equal(true);
});
});
describe("#createImage", () => {
it("should call the underlying footer's createImage", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.Media, "addMedia");
const fileSpy = sinon.spy(file, "addImage");
file.createImage("");
expect(spy.called).to.equal(true);
expect(fileSpy.called).to.equal(true);
});
});
describe("#addChildElement", () => {
it("should call the underlying footer's addChildElement", () => {
const file = new FooterWrapper(new Media(), 1);
const spy = sinon.spy(file.Footer, "addChildElement");
// tslint:disable-next-line:no-any
file.addChildElement({} as any);
expect(spy.called).to.equal(true);
});
});
});

View File

@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components";
import { FooterReferenceType } from "./document";
import { Footer } from "./footer/footer";
import { Image, IMediaData, Media } from "./media";
import { Image, Media } from "./media";
import { ImageParagraph, Paragraph } from "./paragraph";
import { Relationships } from "./relationships";
import { Table } from "./table";
@ -43,29 +43,8 @@ export class FooterWrapper {
this.footer.addChildElement(childElement);
}
public addImageRelationship(image: Buffer, refId: number, width?: number, height?: number): IMediaData {
const mediaData = this.media.addMedia(image, refId, width, height);
this.relationships.createRelationship(
mediaData.referenceId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`,
);
return mediaData;
}
public addHyperlinkRelationship(target: string, refId: number, targetMode?: "External" | undefined): void {
this.relationships.createRelationship(
refId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
target,
targetMode,
);
}
public createImage(image: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number): void {
// TODO
// tslint:disable-next-line:no-any
const mediaData = this.addImageRelationship(image as any, this.relationships.RelationshipCount, width, height);
const mediaData = this.media.addMedia(image, width, height);
this.addImage(new Image(new ImageParagraph(mediaData)));
}

View File

@ -53,7 +53,10 @@ export class Footer extends InitializableXmlComponent {
}
public createTable(rows: number, cols: number): Table {
const table = new Table(rows, cols);
const table = new Table({
rows: rows,
columns: cols,
});
this.addTable(table);
return table;
}

View File

@ -11,8 +11,7 @@ describe("Footnote", () => {
const tree = new Formatter().format(footnote);
expect(Object.keys(tree)).to.deep.equal(["w:footnote"]);
expect(tree["w:footnote"]).to.be.an.instanceof(Array);
expect(tree["w:footnote"][0]).to.deep.equal({ _attr: { "w:type": "separator", "w:id": 1 } });
expect(tree["w:footnote"]).to.deep.equal({ _attr: { "w:type": "separator", "w:id": 1 } });
});
it("should create a footnote without a footnote type", () => {
@ -20,8 +19,7 @@ describe("Footnote", () => {
const tree = new Formatter().format(footnote);
expect(Object.keys(tree)).to.deep.equal(["w:footnote"]);
expect(tree["w:footnote"]).to.be.an.instanceof(Array);
expect(tree["w:footnote"][0]).to.deep.equal({ _attr: { "w:id": 1 } });
expect(tree["w:footnote"]).to.deep.equal({ _attr: { "w:id": 1 } });
});
});
});

View File

@ -9,19 +9,78 @@ import { Table } from "./table";
describe("HeaderWrapper", () => {
describe("#addParagraph", () => {
it("should call the underlying header's addParagraph", () => {
const file = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(file.Header, "addParagraph");
file.addParagraph(new Paragraph());
const wrapper = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(wrapper.Header, "addParagraph");
wrapper.addParagraph(new Paragraph());
expect(spy.called).to.equal(true);
});
});
describe("#addTable", () => {
it("should call the underlying header's addParagraph", () => {
it("should call the underlying header's addTable", () => {
const wrapper = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(wrapper.Header, "addTable");
wrapper.addTable(
new Table({
rows: 1,
columns: 1,
}),
);
expect(spy.called).to.equal(true);
});
});
describe("#createTable", () => {
it("should call the underlying header's createTable", () => {
const wrapper = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(wrapper.Header, "createTable");
wrapper.createTable(1, 1);
expect(spy.called).to.equal(true);
});
});
describe("#createParagraph", () => {
it("should call the underlying header's createParagraph", () => {
const file = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(file.Header, "addTable");
file.addTable(new Table(1, 1));
const spy = sinon.spy(file.Header, "addParagraph");
file.createParagraph();
expect(spy.called).to.equal(true);
});
});
describe("#addImage", () => {
it("should call the underlying header's addImage", () => {
const file = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(file.Header, "addParagraph");
// tslint:disable-next-line:no-any
file.addImage({} as any);
expect(spy.called).to.equal(true);
});
});
describe("#createImage", () => {
it("should call the underlying header's createImage", () => {
const file = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(file.Media, "addMedia");
const fileSpy = sinon.spy(file, "addImage");
file.createImage("");
expect(spy.called).to.equal(true);
expect(fileSpy.called).to.equal(true);
});
});
describe("#addChildElement", () => {
it("should call the underlying header's addChildElement", () => {
const file = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(file.Header, "addChildElement");
// tslint:disable-next-line:no-any
file.addChildElement({} as any);
expect(spy.called).to.equal(true);
});

View File

@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components";
import { HeaderReferenceType } from "./document";
import { Header } from "./header/header";
import { Image, IMediaData, Media } from "./media";
import { Image, Media } from "./media";
import { ImageParagraph, Paragraph } from "./paragraph";
import { Relationships } from "./relationships";
import { Table } from "./table";
@ -43,29 +43,8 @@ export class HeaderWrapper {
this.header.addChildElement(childElement);
}
public addImageRelationship(image: Buffer, refId: number, width?: number, height?: number): IMediaData {
const mediaData = this.media.addMedia(image, refId, width, height);
this.relationships.createRelationship(
mediaData.referenceId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`,
);
return mediaData;
}
public addHyperlinkRelationship(target: string, refId: number, targetMode?: "External" | undefined): void {
this.relationships.createRelationship(
refId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
target,
targetMode,
);
}
public createImage(image: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number): void {
// TODO
// tslint:disable-next-line:no-any
const mediaData = this.addImageRelationship(image as any, this.relationships.RelationshipCount, width, height);
const mediaData = this.media.addMedia(image, width, height);
this.addImage(new Image(new ImageParagraph(mediaData)));
}

View File

@ -64,7 +64,10 @@ export class Header extends InitializableXmlComponent {
}
public createTable(rows: number, cols: number): Table {
const table = new Table(rows, cols);
const table = new Table({
rows: rows,
columns: cols,
});
this.addTable(table);
return table;
}

View File

@ -9,3 +9,5 @@ export * from "./document";
export * from "./styles";
export * from "./table-of-contents";
export * from "./xml-components";
export * from "./header-wrapper";
export * from "./footer-wrapper";

View File

@ -10,7 +10,6 @@ export interface IMediaDataDimensions {
}
export interface IMediaData {
readonly referenceId: number;
readonly stream: Buffer | Uint8Array | ArrayBuffer;
readonly path?: string;
readonly fileName: string;

View File

@ -1,5 +1,6 @@
// tslint:disable:object-literal-key-quotes
import { expect } from "chai";
import { stub } from "sinon";
import { Formatter } from "export/formatter";
@ -20,28 +21,30 @@ describe("Media", () => {
});
it("should ensure the correct relationship id is used when adding image", () => {
// tslint:disable-next-line:no-any
stub(Media as any, "generateId").callsFake(() => "testId");
const file = new File();
const image1 = Media.addImage(file, "test");
const tree = new Formatter().format(image1.Paragraph);
const inlineElements = tree["w:p"][1]["w:r"][1]["w:drawing"][0]["wp:inline"];
const inlineElements = tree["w:p"][0]["w:r"][0]["w:drawing"][0]["wp:inline"];
const graphicData = inlineElements.find((x) => x["a:graphic"]);
expect(graphicData["a:graphic"][1]["a:graphicData"][1]["pic:pic"][2]["pic:blipFill"][0]["a:blip"][0]).to.deep.equal({
expect(graphicData["a:graphic"][1]["a:graphicData"][1]["pic:pic"][2]["pic:blipFill"][0]["a:blip"]).to.deep.equal({
_attr: {
"r:embed": `rId${file.DocumentRelationships.RelationshipCount}`,
"r:embed": `rId{testId.png}`,
cstate: "none",
},
});
const image2 = Media.addImage(file, "test");
const tree2 = new Formatter().format(image2.Paragraph);
const inlineElements2 = tree2["w:p"][1]["w:r"][1]["w:drawing"][0]["wp:inline"];
const inlineElements2 = tree2["w:p"][0]["w:r"][0]["w:drawing"][0]["wp:inline"];
const graphicData2 = inlineElements2.find((x) => x["a:graphic"]);
expect(graphicData2["a:graphic"][1]["a:graphicData"][1]["pic:pic"][2]["pic:blipFill"][0]["a:blip"][0]).to.deep.equal({
expect(graphicData2["a:graphic"][1]["a:graphicData"][1]["pic:pic"][2]["pic:blipFill"][0]["a:blip"]).to.deep.equal({
_attr: {
"r:embed": `rId${file.DocumentRelationships.RelationshipCount}`,
"r:embed": `rId{testId.png}`,
cstate: "none",
},
});
@ -53,9 +56,8 @@ describe("Media", () => {
// tslint:disable-next-line:no-any
(Media as any).generateId = () => "test";
const image = new Media().addMedia("", 1);
const image = new Media().addMedia("");
expect(image.fileName).to.equal("test.png");
expect(image.referenceId).to.equal(1);
expect(image.dimensions).to.deep.equal({
pixels: {
x: 100,
@ -74,7 +76,7 @@ describe("Media", () => {
// tslint:disable-next-line:no-any
(Media as any).generateId = () => "test";
const image = new Media().addMedia("", 1);
const image = new Media().addMedia("");
expect(image.stream).to.be.an.instanceof(Uint8Array);
});
});
@ -85,12 +87,11 @@ describe("Media", () => {
(Media as any).generateId = () => "test";
const media = new Media();
media.addMedia("", 1);
media.addMedia("");
const image = media.getMedia("test.png");
expect(image.fileName).to.equal("test.png");
expect(image.referenceId).to.equal(1);
expect(image.dimensions).to.deep.equal({
pixels: {
x: 100,
@ -116,7 +117,7 @@ describe("Media", () => {
(Media as any).generateId = () => "test";
const media = new Media();
media.addMedia("", 1);
media.addMedia("");
const array = media.Array;
expect(array).to.be.an.instanceof(Array);
@ -124,7 +125,6 @@ describe("Media", () => {
const image = array[0];
expect(image.fileName).to.equal("test.png");
expect(image.referenceId).to.equal(1);
expect(image.dimensions).to.deep.equal({
pixels: {
x: 100,

View File

@ -4,11 +4,6 @@ import { ImageParagraph } from "../paragraph";
import { IMediaData } from "./data";
import { Image } from "./image";
interface IHackedFile {
// tslint:disable-next-line:readonly-keyword
currentRelationshipId: number;
}
export class Media {
public static addImage(
file: File,
@ -18,14 +13,7 @@ export class Media {
drawingOptions?: IDrawingOptions,
): Image {
// Workaround to expose id without exposing to API
const exposedFile = (file as {}) as IHackedFile;
const mediaData = file.Media.addMedia(buffer, exposedFile.currentRelationshipId++, width, height);
file.DocumentRelationships.createRelationship(
mediaData.referenceId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`,
);
const mediaData = file.Media.addMedia(buffer, width, height);
return new Image(new ImageParagraph(mediaData, drawingOptions));
}
@ -57,17 +45,11 @@ export class Media {
return data;
}
public addMedia(
buffer: Buffer | string | Uint8Array | ArrayBuffer,
referenceId: number,
width: number = 100,
height: number = 100,
): IMediaData {
public addMedia(buffer: Buffer | string | Uint8Array | ArrayBuffer, width: number = 100, height: number = 100): IMediaData {
const key = `${Media.generateId()}.png`;
return this.createMedia(
key,
referenceId,
{
width: width,
height: height,
@ -78,7 +60,6 @@ export class Media {
private createMedia(
key: string,
relationshipsCount: number,
dimensions: { readonly width: number; readonly height: number },
data: Buffer | string | Uint8Array | ArrayBuffer,
filePath?: string,
@ -86,7 +67,6 @@ export class Media {
const newData = typeof data === "string" ? this.convertDataURIToBinary(data) : data;
const imageData: IMediaData = {
referenceId: relationshipsCount,
stream: newData,
path: filePath,
fileName: key,

View File

@ -1,3 +1,4 @@
export * from "./numbering";
export * from "./abstract-numbering";
export * from "./level";
export * from "./num";

View File

@ -7,6 +7,8 @@ import { LevelForOverride } from "./level";
import { Num } from "./num";
import { Numbering } from "./numbering";
import { EMPTY_OBJECT } from "file/xml-components";
describe("Numbering", () => {
let numbering: Numbering;
@ -22,7 +24,7 @@ describe("Numbering", () => {
expect(abstractNums).to.have.lengthOf(1);
expect(abstractNums[0]["w:abstractNum"]).to.deep.include.members([
{ _attr: { "w:abstractNumId": 0, "w15:restartNumberingAfterBreak": 0 } },
{ "w:multiLevelType": [{ _attr: { "w:val": "hybridMultilevel" } }] },
{ "w:multiLevelType": { _attr: { "w:val": "hybridMultilevel" } } },
]);
abstractNums
@ -37,9 +39,9 @@ describe("Numbering", () => {
{ "w:numFmt": [{ _attr: { "w:val": "bullet" } }] },
]);
// Once chai 4.0.0 lands and #644 is resolved, we can add the following to the test:
// {"w:lvlText": [{"_attr": {"w:val": "•"}}]},
// {"w:rPr": [{"w:rFonts": [{"_attr": {"w:ascii": "Symbol", "w:cs": "Symbol", "w:eastAsia": "Symbol", "w:hAnsi": "Symbol", "w:hint": "default"}}]}]},
// {"w:pPr": [{"_attr": {}},
// {"w:lvlText": {"_attr": {"w:val": "•"}}},
// {"w:rPr": [{"w:rFonts": {"_attr": {"w:ascii": "Symbol", "w:cs": "Symbol", "w:eastAsia": "Symbol", "w:hAnsi": "Symbol", "w:hint": "default"}}}]},
// {"w:pPr": [
// {"w:ind": [{"_attr": {"w:left": 720, "w:hanging": 360}}]}]},
});
});
@ -65,7 +67,7 @@ describe("Numbering", () => {
expect(n).to.be.instanceof(Num);
const tree = new Formatter().format(numbering);
const serializedN = tree["w:numbering"].find((obj) => obj["w:num"] && obj["w:num"][0]._attr["w:numId"] === n.id);
expect(serializedN["w:num"][1]["w:abstractNumId"][0]._attr["w:val"]).to.equal(a2.id);
expect(serializedN["w:num"][1]["w:abstractNumId"]._attr["w:val"]).to.equal(a2.id);
});
it("assigns a unique ID to each concrete numbering it creates", () => {
@ -89,10 +91,10 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(3, "lowerLetter", "%1)", "end");
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } });
expect(tree["w:lvl"]).to.include({ "w:start": [{ _attr: { "w:val": 1 } }] });
expect(tree["w:lvl"]).to.include({ "w:lvlJc": [{ _attr: { "w:val": "end" } }] });
expect(tree["w:lvl"]).to.include({ "w:numFmt": [{ _attr: { "w:val": "lowerLetter" } }] });
expect(tree["w:lvl"]).to.include({ "w:lvlText": [{ _attr: { "w:val": "%1)" } }] });
expect(tree["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } });
expect(tree["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "end" } } });
expect(tree["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": "lowerLetter" } } });
expect(tree["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } });
});
it("uses 'start' as the default alignment", () => {
@ -100,10 +102,10 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(3, "lowerLetter", "%1)");
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({ _attr: { "w:ilvl": 3, "w15:tentative": 1 } });
expect(tree["w:lvl"]).to.include({ "w:start": [{ _attr: { "w:val": 1 } }] });
expect(tree["w:lvl"]).to.include({ "w:lvlJc": [{ _attr: { "w:val": "start" } }] });
expect(tree["w:lvl"]).to.include({ "w:numFmt": [{ _attr: { "w:val": "lowerLetter" } }] });
expect(tree["w:lvl"]).to.include({ "w:lvlText": [{ _attr: { "w:val": "%1)" } }] });
expect(tree["w:lvl"]).to.include({ "w:start": { _attr: { "w:val": 1 } } });
expect(tree["w:lvl"]).to.include({ "w:lvlJc": { _attr: { "w:val": "start" } } });
expect(tree["w:lvl"]).to.include({ "w:numFmt": { _attr: { "w:val": "lowerLetter" } } });
expect(tree["w:lvl"]).to.include({ "w:lvlText": { _attr: { "w:val": "%1)" } } });
});
describe("formatting methods: paragraph properties", () => {
@ -112,7 +114,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerLetter", "%0.").indent({ left: 720 });
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:pPr": [{ "w:ind": [{ _attr: { "w:left": 720 } }] }],
"w:pPr": [{ "w:ind": { _attr: { "w:left": 720 } } }],
});
});
@ -121,7 +123,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerLetter", "%0.").spacing({ before: 50, after: 150 });
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:pPr": [{ "w:spacing": [{ _attr: { "w:before": 50, "w:after": 150 } }] }],
"w:pPr": [{ "w:spacing": { _attr: { "w:before": 50, "w:after": 150 } } }],
});
});
@ -130,7 +132,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerLetter", "%0.").center();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:pPr": [{ "w:jc": [{ _attr: { "w:val": "center" } }] }],
"w:pPr": [{ "w:jc": { _attr: { "w:val": "center" } } }],
});
});
@ -139,7 +141,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.", "left").left();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:pPr": [{ "w:jc": [{ _attr: { "w:val": "left" } }] }],
"w:pPr": [{ "w:jc": { _attr: { "w:val": "left" } } }],
});
});
@ -148,7 +150,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").right();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:pPr": [{ "w:jc": [{ _attr: { "w:val": "right" } }] }],
"w:pPr": [{ "w:jc": { _attr: { "w:val": "right" } } }],
});
});
@ -157,7 +159,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").justified();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:pPr": [{ "w:jc": [{ _attr: { "w:val": "both" } }] }],
"w:pPr": [{ "w:jc": { _attr: { "w:val": "both" } } }],
});
});
@ -170,16 +172,14 @@ describe("AbstractNumbering", () => {
{
"w:pBdr": [
{
"w:bottom": [
{
_attr: {
"w:color": "auto",
"w:space": "1",
"w:val": "single",
"w:sz": "6",
},
"w:bottom": {
_attr: {
"w:color": "auto",
"w:space": "1",
"w:val": "single",
"w:sz": "6",
},
],
},
},
],
},
@ -194,7 +194,7 @@ describe("AbstractNumbering", () => {
expect(tree["w:lvl"]).to.include({
"w:pPr": [
{
"w:tabs": [{ "w:tab": [{ _attr: { "w:val": "left", "w:pos": 1200 } }] }],
"w:tabs": [{ "w:tab": { _attr: { "w:val": "left", "w:pos": 1200 } } }],
},
],
});
@ -207,7 +207,7 @@ describe("AbstractNumbering", () => {
expect(tree["w:lvl"]).to.include({
"w:pPr": [
{
"w:tabs": [{ "w:tab": [{ _attr: { "w:val": "right", "w:pos": 9026 } }] }],
"w:tabs": [{ "w:tab": { _attr: { "w:val": "right", "w:pos": 9026 } } }],
},
],
});
@ -218,7 +218,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").keepLines();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:pPr": [{ "w:keepLines": [] }],
"w:pPr": [{ "w:keepLines": EMPTY_OBJECT }],
});
});
@ -227,7 +227,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").keepNext();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:pPr": [{ "w:keepNext": [] }],
"w:pPr": [{ "w:keepNext": EMPTY_OBJECT }],
});
});
});
@ -238,7 +238,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").size(24);
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:sz": [{ _attr: { "w:val": 24 } }] }],
"w:rPr": [{ "w:sz": { _attr: { "w:val": 24 } } }],
});
});
@ -247,7 +247,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").smallCaps();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:smallCaps": [{ _attr: { "w:val": true } }] }],
"w:rPr": [{ "w:smallCaps": { _attr: { "w:val": true } } }],
});
});
@ -256,7 +256,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").allCaps();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:caps": [{ _attr: { "w:val": true } }] }],
"w:rPr": [{ "w:caps": { _attr: { "w:val": true } } }],
});
});
@ -265,7 +265,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").strike();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:strike": [{ _attr: { "w:val": true } }] }],
"w:rPr": [{ "w:strike": { _attr: { "w:val": true } } }],
});
});
@ -274,7 +274,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").doubleStrike();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:dstrike": [{ _attr: { "w:val": true } }] }],
"w:rPr": [{ "w:dstrike": { _attr: { "w:val": true } } }],
});
});
@ -283,7 +283,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").subScript();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:vertAlign": [{ _attr: { "w:val": "subscript" } }] }],
"w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "subscript" } } }],
});
});
@ -292,7 +292,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").superScript();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:vertAlign": [{ _attr: { "w:val": "superscript" } }] }],
"w:rPr": [{ "w:vertAlign": { _attr: { "w:val": "superscript" } } }],
});
});
@ -302,7 +302,7 @@ describe("AbstractNumbering", () => {
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [
{ "w:rFonts": [{ _attr: { "w:ascii": "Times", "w:cs": "Times", "w:eastAsia": "Times", "w:hAnsi": "Times" } }] },
{ "w:rFonts": { _attr: { "w:ascii": "Times", "w:cs": "Times", "w:eastAsia": "Times", "w:hAnsi": "Times" } } },
],
});
});
@ -312,7 +312,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").bold();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:b": [{ _attr: { "w:val": true } }] }],
"w:rPr": [{ "w:b": { _attr: { "w:val": true } } }],
});
});
@ -321,7 +321,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").italics();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:i": [{ _attr: { "w:val": true } }] }],
"w:rPr": [{ "w:i": { _attr: { "w:val": true } } }],
});
});
@ -331,7 +331,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline();
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:u": [{ _attr: { "w:val": "single" } }] }],
"w:rPr": [{ "w:u": { _attr: { "w:val": "single" } } }],
});
});
@ -340,7 +340,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline("double");
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:u": [{ _attr: { "w:val": "double" } }] }],
"w:rPr": [{ "w:u": { _attr: { "w:val": "double" } } }],
});
});
@ -349,7 +349,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").underline("double", "005599");
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:u": [{ _attr: { "w:val": "double", "w:color": "005599" } }] }],
"w:rPr": [{ "w:u": { _attr: { "w:val": "double", "w:color": "005599" } } }],
});
});
});
@ -359,7 +359,7 @@ describe("AbstractNumbering", () => {
const level = abstractNumbering.createLevel(0, "lowerRoman", "%0.").color("123456");
const tree = new Formatter().format(level);
expect(tree["w:lvl"]).to.include({
"w:rPr": [{ "w:color": [{ _attr: { "w:val": "123456" } }] }],
"w:rPr": [{ "w:color": { _attr: { "w:val": "123456" } } }],
});
});
});
@ -388,20 +388,12 @@ describe("concrete numbering", () => {
},
},
{
"w:lvl": [
{
_attr: {
"w:ilvl": 3,
"w15:tentative": 1,
},
"w:lvl": {
_attr: {
"w:ilvl": 3,
"w15:tentative": 1,
},
{
"w:pPr": [],
},
{
"w:rPr": [],
},
],
},
},
],
});
@ -418,29 +410,19 @@ describe("concrete numbering", () => {
},
},
{
"w:startOverride": [
{
_attr: {
"w:val": 9,
},
"w:startOverride": {
_attr: {
"w:val": 9,
},
],
},
},
{
"w:lvl": [
{
_attr: {
"w:ilvl": 1,
"w15:tentative": 1,
},
"w:lvl": {
_attr: {
"w:ilvl": 1,
"w15:tentative": 1,
},
{
"w:pPr": [],
},
{
"w:rPr": [],
},
],
},
},
],
});
@ -454,7 +436,7 @@ describe("concrete numbering", () => {
"w:lvlOverride": [
{ _attr: { "w:ilvl": 1 } },
{
"w:lvl": [{ _attr: { "w15:tentative": 1, "w:ilvl": 1 } }, { "w:pPr": [] }, { "w:rPr": [] }],
"w:lvl": { _attr: { "w15:tentative": 1, "w:ilvl": 1 } },
},
],
});

View File

@ -1,6 +1,6 @@
import { assert } from "chai";
import { assert, expect } from "chai";
import { Utility } from "tests/utility";
import { Formatter } from "export/formatter";
import { ThematicBreak } from "./border";
@ -28,14 +28,21 @@ describe("ThematicBreak", () => {
});
it("should create a Thematic Break with correct border properties", () => {
const newJson = Utility.jsonify(thematicBreak);
const attributes = {
color: "auto",
space: "1",
val: "single",
sz: "6",
};
assert.equal(JSON.stringify(newJson.root[0].root[0].root), JSON.stringify(attributes));
const tree = new Formatter().format(thematicBreak);
expect(tree).to.deep.equal({
"w:pBdr": [
{
"w:bottom": {
_attr: {
"w:color": "auto",
"w:space": "1",
"w:sz": "6",
"w:val": "single",
},
},
},
],
});
});
});
});

Some files were not shown because too many files have changed in this diff Show More