Compare commits

...

191 Commits
4.0.0 ... 4.7.0

Author SHA1 Message Date
8dc590746b Version bump 2019-01-16 00:15:27 +00:00
de5f5c9a77 Add documentation 2019-01-16 00:13:18 +00:00
ca5f6a56a5 Write tests 2019-01-15 21:40:19 +00:00
8f6984580a Add Number of pages element 2019-01-15 02:09:38 +00:00
da0fa86345 Version bump 2019-01-10 02:37:40 +00:00
78b310e1dd Add Add createFirstPageFooter and createEvenPageFooter 2019-01-10 02:36:42 +00:00
4541d7c977 Text wrapping and borders for images 2019-01-10 02:10:20 +00:00
a37ff90bd7 Version bump 2019-01-09 01:53:12 +00:00
9998ddfcc9 Add createEvenPageHeader method 2019-01-09 01:52:20 +00:00
12ed54c9fd Add floating image support and documentation 2019-01-09 01:16:47 +00:00
3a1a21e498 Version bump 2018-12-10 19:27:51 +00:00
e5bfa99b92 Add correct docx mime type 2018-12-10 19:22:23 +00:00
f640f17fa6 Merge pull request #193 from dolanmiu/4.4.0
Version bump
2018-11-08 12:05:31 +00:00
494a32d45d Merge pull request #195 from kunizial/patch-1
Update numbering.md
2018-11-08 12:05:17 +00:00
8040a455be Update numbering.md
Fixed Typo
2018-11-08 11:09:09 +01:00
f091cff7c9 Version bump 2018-11-07 01:06:59 +00:00
401ef7336b Merge pull request #190 from dolanmiu/feat/more-tests
Introduce some functional programming techniques
2018-11-02 03:12:28 +00:00
18134519be Edit threshold 2018-11-02 03:00:07 +00:00
7980f14efb Introduce some functional programming techniques 2018-11-02 02:51:57 +00:00
40d6a41305 Merge pull request #189 from dolanmiu/feat/more-tests
More tests
2018-11-02 00:53:54 +00:00
9cfd835171 More tests 2018-11-02 00:42:49 +00:00
95a9f592eb Merge pull request #143 from amitm02/importDotx
use .dotx files as an initial template for a new (document) file
2018-11-01 02:31:33 +00:00
61411fd0f3 Add tests 2018-11-01 02:22:32 +00:00
a84eb16392 Merge branch 'master' into importDotx
# Conflicts:
#	src/import-dotx/import-dotx.ts
2018-10-31 22:09:36 +00:00
3355a6f472 Added back tests and method 2018-10-31 21:57:10 +00:00
071a8ea9f7 Merge pull request #187 from dolanmiu/feat/clean-imports
Feat/clean imports
2018-10-26 21:00:42 +01:00
ea3777d28b Better naming 2018-10-26 20:30:42 +01:00
3346b97ee7 Clean up 2018-10-26 20:23:26 +01:00
2b834a75a8 Add codecov badge 2018-10-26 20:22:33 +01:00
21df53d547 Clean up 2018-10-26 20:16:18 +01:00
9143c1c2c1 Add nyc support 2018-10-26 20:11:40 +01:00
83cab7563d Add nyc to travis 2018-10-26 02:20:10 +01:00
b05fbe7f6e Merge pull request #185 from dolanmiu/feat/clean-imports
Add codecov and add more tests to travis
2018-10-26 02:12:52 +01:00
e6a57738f4 Demo 25 does not exist 2018-10-26 02:07:25 +01:00
cc6e35165a Add codecov and add more tests to travis 2018-10-26 02:00:45 +01:00
7791ddf76e Merge pull request #184 from dolanmiu/feat/clean-imports
Clean imports
2018-10-26 01:16:23 +01:00
4742cf0f3f Clean imports 2018-10-26 01:04:07 +01:00
8858970491 Merge pull request #182 from dolanmiu/feat/table-clean-up
Tidied up table components
2018-10-24 14:33:10 +01:00
f7d18bfead Tidied up table components 2018-10-23 23:44:50 +01:00
0ce1b7fa15 Merge pull request #181 from dolanmiu/feat/float-table
Created float positioning for tables
2018-10-23 21:40:33 +01:00
4633592711 Improve API 2018-10-23 20:03:29 +01:00
54697ab6b1 Created float positioning for tables 2018-10-23 09:08:43 -02:00
1eed844b9a Merge pull request #179 from dolanmiu/feat/import-dotx
Feat/import dotx
2018-10-23 00:47:18 +01:00
dd89fe2463 Fix casing 2018-10-23 00:41:18 +01:00
9c66db97ff Use single media instead of multiple 2018-10-23 00:31:51 +01:00
fea6afdfe4 Remove npm dependency 2018-10-22 20:27:32 +01:00
6ec2e742ef Merge branch 'master' into importDotx 2018-10-22 20:25:34 +01:00
3f80b054fc Merge pull request #178 from dolanmiu/bugfix/correct-toc-instruction
changed comma by semicolon to work in Word 2016.
2018-10-22 03:37:54 +01:00
96d81873d8 forgot to fix the test 2018-10-21 19:56:31 -02:00
c429ae9920 changed comma by semicolon to work in Word 2016. 2018-10-21 19:37:37 -02:00
6b4e769f48 Merge branch 'master' of https://github.com/dolanmiu/docx 2018-10-21 05:16:32 +01:00
b67a9de0e9 Angular README example 2018-10-21 05:14:14 +01:00
ac9f65a068 Merge pull request #177 from dolanmiu/feat/guidelines
Contribution guidelines improve
2018-10-21 04:51:45 +01:00
7e8ebb2af2 Contribution guidelines improve 2018-10-21 04:50:57 +01:00
f53fe2f881 Merge pull request #175 from dolanmiu/feat/sequential-field
Created classes and methods needed to create Sequential Identifiers
2018-10-21 02:00:20 +01:00
6fdd88527a Merge pull request #176 from dolanmiu/bugfix/correct-toc-instruction
fixed TOC instruction that was missing quotes for some switches
2018-10-20 17:36:44 +01:00
b6f431e14d fixed TOC instruction that was missing quotes for some switches 2018-10-20 09:17:37 -03:00
23dee01f06 Created classes and methods needed to create Sequential Identifiers 2018-10-19 16:50:51 -03:00
5532f91423 Merge branch 'importxmljs' into importDotx 2018-10-17 09:17:42 +03:00
c849d5f3e5 fix demp30 2018-10-17 09:16:55 +03:00
3f3fd05cb1 works 2018-10-17 09:15:32 +03:00
a5bedf9a5b Add break after cases in switch 2018-10-17 01:40:17 +01:00
9d9dd62f00 c 2018-10-16 11:28:25 +03:00
a466578467 Version bump 2018-10-15 23:34:35 +01:00
3a9420fedf Merge pull request #149 from dolanmiu/feat/grid-span
Feat/grid span
2018-10-15 23:23:20 +01:00
8ac19a83b2 Rename demos 2018-10-15 22:21:40 +01:00
9f0b2f7074 Improve API 2018-10-15 21:54:33 +01:00
e02ac43c07 Version bump 2018-10-10 20:39:11 +01:00
97f76fb62c Change to new header API 2018-10-05 01:33:17 +01:00
3508fd97ec Merge branch 'importDotx' of https://github.com/amitm02/docx into importDotx 2018-10-05 01:21:29 +01:00
90f6f68693 Changed protected to private 2018-10-05 00:20:43 +01:00
733775d3b9 Rename demo 2018-10-05 00:08:14 +01:00
f3aa6a9203 detect if page title is defined in tempalte 2018-10-02 17:52:55 +03:00
ffdcc7baca hande hyplerlink references in header or footers 2018-10-02 16:17:26 +03:00
a1e20f4c9a handle templates with no headers or foolters 2018-10-02 16:02:28 +03:00
ccffdad4c0 Merge pull request #171 from amitm02/master
Contextual spacing
2018-09-26 15:57:16 +01:00
2fb5845501 contextual spacing 2018-09-26 17:47:17 +03:00
ddd84a1765 Merge pull request #3 from dolanmiu/master
from base
2018-09-26 17:27:41 +03:00
048ae6a58c extract styles from dotx 2018-09-26 14:33:05 +03:00
fafa54e4c9 bug fix (demo29 corrupted) 2018-09-26 13:10:21 +03:00
60dbb32e9e Revert "Revert "Add back relationships""
This reverts commit 52b07fd9cb.
2018-09-26 12:03:52 +03:00
52b07fd9cb Revert "Add back relationships"
This reverts commit 6e0c12afb3.
2018-09-26 10:12:40 +03:00
f6a13aed86 Merge pull request #169 from dolanmiu/feat/table-of-contents
Feat/table of contents
2018-09-26 02:28:44 +01:00
2da3ba0262 Rename variables 2018-09-26 02:17:39 +01:00
e08c7cbbfb Add table of contents to side bar 2018-09-26 02:12:10 +01:00
a6de5d8a21 Merge pull request #168 from dolanmiu/feat/table-of-contents
Change documentation properties to options
2018-09-26 02:10:03 +01:00
8ec5bc05e0 Export file properties 2018-09-26 02:03:32 +01:00
6e0c12afb3 Add back relationships 2018-09-26 00:24:33 +01:00
466e880bfc Merge from master 2018-09-25 23:46:55 +01:00
00a20b7cfc Merge pull request #166 from dolanmiu/feat/table-of-contents
Feat/table of contents
2018-09-25 23:32:58 +01:00
f27c95191b Change documentation properties to options 2018-09-25 21:19:04 +01:00
c140d2c37c ITableOfContentsProperties to ITableOfContentsOptions 2018-09-25 21:09:30 +01:00
3a42f2a2f0 Make API simplier with interfaces 2018-09-25 20:05:35 +01:00
00efedaa09 Fix spelling and linting and improve readme 2018-09-25 19:49:44 +01:00
72e3d229dc unnecessary comment removed 2018-09-25 02:36:00 -03:00
b12d6ef484 initial documentation 2018-09-25 02:35:08 -03:00
8ee6fd3e67 demo updated 2018-09-25 01:33:44 -03:00
808c5b00a0 table of contents with all the options 2018-09-25 01:18:47 -03:00
4de6b51e76 removed getStyles and clearPageBreak that became useless 2018-09-25 00:10:27 -03:00
0a8feca6ab Rename importDocx to import-dotx 2018-09-24 22:00:08 +01:00
4ca81df401 TOC content generation aborted 2018-09-21 11:16:14 -03:00
8b463b3bb6 updated clone deep dependency and make fields dirty to be updated when word is opened 2018-09-21 10:26:28 -03:00
17d696e33a merge with master, demo27 became demo28 2018-09-21 07:40:58 -03:00
2f59867db6 Merge branch 'feat/table-of-contents' of https://github.com/dolanmiu/docx into feat/table-of-contents 2018-09-21 07:37:26 -03:00
90f3da74cf Version bump 2018-09-20 21:04:21 +01:00
28d8659cb5 Merge pull request #161 from bre7/patch-1
Improve fluency (dsl like)
2018-09-20 21:03:38 +01:00
5d1740a2ef Style fixes 2018-09-20 16:54:58 -03:00
7aa44568d8 Improve fluency (dsl like)
addRunProperty / addParagraphProperty should also return this
2018-09-20 15:51:57 -03:00
bf1f702e5a created a clone method for xml-components 2018-09-20 15:13:18 -03:00
78ad3e340a Merge pull request #160 from bre7/patch-1
Added charSpacing to docs
2018-09-20 18:46:30 +01:00
63a965beab Added charSpacing 2018-09-20 14:00:49 -03:00
815354e06f Merge pull request #159 from bre7/master
Added character spacing attribute
2018-09-20 16:27:21 +01:00
1cff104bae correct sdt objects of table of contents 2018-09-20 10:47:10 -03:00
4805efad2e organized imports 2018-09-20 10:31:49 -03:00
12e2ae9e91 making leader a option field and test improvments 2018-09-20 10:30:16 -03:00
c07b5cf709 added settings.xml back 2018-09-20 10:11:59 -03:00
0684738ec2 removed lodash 2018-09-20 07:09:17 -03:00
f2b50478bf Fix tests and use proper types for disregarding XMLComponent 2018-09-20 00:41:57 +01:00
fc71ebdfef Refactor code 2018-09-19 23:07:37 +01:00
10114bb12d Remove TemplatedFile 2018-09-19 23:04:34 +01:00
6da3efdacc Added character spacing attribute 2018-09-19 18:41:55 -03:00
7cd8864fb9 added stdPr and stdContent to table of contents 2018-09-19 11:01:07 -03:00
a9c69664c7 Merge pull request #156 from bre7/master
Added mirror margins option
2018-09-19 12:40:31 +01:00
50569224c3 Fixed tests (included mirrorMargins attr) 2018-09-18 23:50:10 -03:00
e6d4741955 Merge branch 'master' of https://github.com/dolanmiu/docx into importDotx
# Conflicts:
#	src/file/file.ts
2018-09-19 01:53:52 +01:00
4d7387524c Rename demo 2018-09-19 01:30:25 +01:00
877aa325bb Merge pull request #153 from felipeochoa/fix-109
Fix 109
2018-09-19 01:25:32 +01:00
eb797d8986 Ignore Yarn lock & IntelliJ ide 2018-09-18 21:03:50 -03:00
fcb542471b Added mirror margins options 2018-09-18 21:03:20 -03:00
5fe4405d76 Add templated file 2018-09-19 00:37:11 +01:00
baf0f17bd6 added a method for clear the page breaks of a paragraph 2018-09-18 13:53:30 -03:00
8e911698a5 generating the content for a table of contents 2018-09-18 05:24:19 -03:00
0eb36be053 there is no need for the settings.xml file anymore 2018-09-17 22:09:05 -03:00
79dffc873a Organise imports 2018-09-18 00:51:35 +01:00
26ee12759c Using custom parseOptions 2018-09-17 23:54:19 +01:00
2e00634bc4 Fix tests but break demo 27 2018-09-17 22:06:38 +01:00
d63e6bf6b1 Revert back body test 2018-09-17 21:44:12 +01:00
a95366e54e Fix demo 27 2018-09-17 21:18:27 +01:00
1c376abeb6 Fix demo 8 2018-09-17 21:15:24 +01:00
353d888abd Simplify code using Spreading 2018-09-17 20:27:43 +01:00
b6bd532295 Use dedicated XmlComponent rather than polute pure one 2018-09-17 20:22:11 +01:00
c5b004166d Add back comp null check 2018-09-17 20:18:31 +01:00
2f43600daf Remove MyDocument 2018-09-17 20:09:31 +01:00
b39c7ce323 Fix naming 2018-09-17 20:06:51 +01:00
5b00279996 Remove space 2018-09-17 19:44:21 +01:00
53b24965aa Merge pull request #152 from felipeochoa/fix-typo-table-properties
Fix typo in private property
2018-09-17 19:04:58 +01:00
edce1bef92 Allow creating Styles and assigning them to File.Styles
Closes #109
2018-09-17 12:48:21 -05:00
e0d54d3af3 Fix typo in private property 2018-09-17 12:41:17 -05:00
d1d1e01aff lint fix 2018-09-17 11:29:01 +03:00
980bc597e2 test fixes 2018-09-17 11:24:56 +03:00
a05c5edd49 Revert back .vscode folder 2018-09-14 02:44:47 +01:00
985452f5f4 Use proper name 2018-09-14 02:39:52 +01:00
482674b3b3 Fix spelling error 2018-09-14 02:37:03 +01:00
fcbfed9068 Revert code 2018-09-14 02:33:36 +01:00
a9167b4809 Update demos 2018-09-13 01:54:37 +01:00
5b28eb0d00 Update logo to be blinking 2018-09-12 21:19:33 +01:00
f1b176670c Add more table demos 2018-09-12 21:03:06 +01:00
11ce9a5206 Add horizontal span 2018-09-12 21:01:52 +01:00
8b667b8d4c Merge pull request #148 from dolanmiu/feat/logo
Updated logo dimensions
2018-09-11 19:18:31 +01:00
a601a82a37 Updated logo dimensions 2018-09-11 19:16:47 +01:00
dcf3767a05 Merge pull request #147 from dolanmiu/feat/logo
Add logo
2018-09-11 19:08:27 +01:00
a9278009f8 Merge branch 'feat/logo' of https://github.com/dolanmiu/docx into feat/logo 2018-09-11 19:04:19 +01:00
5220f9e07c Add correct image 2018-09-11 19:03:53 +01:00
0b88064ca2 Update README.md 2018-09-11 19:03:29 +01:00
2233ccb968 Add logo 2018-09-11 19:00:52 +01:00
311fde01a1 Update Readme 2018-09-10 22:44:44 +01:00
146d0ad9e5 Moved addTableOfContents to File and creating a settings.xml and applying updateFields=true when there is a table of contents 2018-09-10 10:01:26 -03:00
385ad92331 Fix linting errors 2018-09-07 21:48:59 +01:00
571f8b526b Fix demo 2018-09-06 09:18:52 +01:00
7688aa99f6 Fix linting errors 2018-09-06 09:09:36 +01:00
4994bca34c Fix some linting errors 2018-09-06 08:30:23 +01:00
1a3603dbfb fix demo 16 2018-09-05 10:02:42 +03:00
aedfca377f elements need to be inside runs 2018-09-04 18:22:08 -03:00
7926f6c189 escape bars 2018-09-04 17:29:24 -03:00
0303a9f553 Merge pull request #141 from XappMedia/min-eight
Minimum Node v8 Support
2018-09-04 18:26:34 +01:00
e9a007d446 create default properties for table-of-contents instruction 2018-09-04 12:05:41 -03:00
e382dbff84 update demp template 2018-09-04 17:32:16 +03:00
e08be3d7a4 update template 2018-09-04 17:31:30 +03:00
03c4190c2c works! 2018-09-04 17:16:31 +03:00
12c1f82efe demo27 for TableOfContents 2018-09-04 11:03:17 -03:00
b1711ae293 first simple version of TOC working 2018-09-03 11:20:36 -03:00
a367951d07 moved Begin, Separate and End from page-number.ts to field.ts 2018-09-03 10:54:53 -03:00
c55f82c425 test improvment 2018-09-03 10:48:50 -03:00
2dab11e8b3 minimum 8 2018-08-31 11:25:05 -04:00
d1044d262e table of contents extending paragraph 2018-08-31 10:22:40 -03:00
010fde6258 commit 2018-08-29 18:36:48 +03:00
8d83219bb6 wrote some initial code 2018-08-29 12:06:01 -03:00
a710483918 Merge pull request #2 from dolanmiu/master
merge from upstream
2018-08-27 18:11:47 +03:00
4f8ecf631b Merge branch 'master' into feat/table-of-contents 2018-08-23 06:21:01 -03:00
a6a8012b39 wrote some inital code 2018-08-22 10:30:19 -03:00
d5b6225a90 starting table of contents 2018-08-21 07:19:46 -03:00
242 changed files with 5169 additions and 1767 deletions

10
.gitignore vendored
View File

@ -48,8 +48,18 @@ docs/.nojekyll
!.vscode/extensions.json
.history
# IntelliJ
.idea
# Lock files
package-lock.json
yarn.lock
# Documents
My Document.docx
# Temporary folder
tmp
# nyc
.nyc_output

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
v8

25
.nycrc Normal file
View File

@ -0,0 +1,25 @@
{
"check-coverage": true,
"lines": 87.54,
"functions": 83.61,
"branches": 72.57,
"statements": 87.32,
"include": [
"src/**/*.ts"
],
"exclude": [
"src/**/*.spec.ts"
],
"reporter": [
"lcov",
"text",
"json"
],
"extension": [
".ts"
],
"cache": true,
"all": true,
"instrument": false,
"sourceMap": true
}

View File

@ -3,9 +3,10 @@ node_js:
- 9
install:
- npm install
- npm install -g codecov
script:
- npm run lint
- npm test
- npm run test.coverage
- npm run style
- npm run build
- npm run ts-node -- ./demo/demo1.ts
@ -32,12 +33,23 @@ script:
- npm run ts-node -- ./demo/demo22.ts
- npm run ts-node -- ./demo/demo23.ts
- npm run ts-node -- ./demo/demo24.ts
# - npm run ts-node -- ./demo/demo25.ts
- npm run ts-node -- ./demo/demo26.ts
- npm run ts-node -- ./demo/demo27.ts
- npm run ts-node -- ./demo/demo28.ts
- npm run ts-node -- ./demo/demo29.ts
- npm run ts-node -- ./demo/demo30.ts
- npm run ts-node -- ./demo/demo31.ts
- npm run ts-node -- ./demo/demo32.ts
- npm run ts-node -- ./demo/demo33.ts
- npm run ts-node -- ./demo/demo34.ts
after_failure:
- "cat /home/travis/builds/dolanmiu/docx/npm-debug.log"
after_success:
- npm run typedoc
- echo "docx.js.org" > docs/.nojekyll
- echo "docx.js.org" > docs/CNAME
- codecov
deploy:
provider: pages
skip-cleanup: true

24
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,24 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "typescript",
"tsconfig": "tsconfig.json",
"option": "watch",
"problemMatcher": [
"$tsc-watch"
]
},
{
"type": "npm",
"script": "ts-node",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View File

@ -1,5 +1,5 @@
<p align="center">
<img alt="clippy the assistant" src="http://i60.tinypic.com/339pvtt.png">
<img alt="clippy the assistant" src="https://i.imgur.com/37uBGhO.gif">
</p>
<p align="center">
@ -15,13 +15,27 @@
[![Known Vulnerabilities][snky-image]][snky-url]
[![Chat on Gitter][gitter-image]][gitter-url]
[![PRs Welcome][pr-image]][pr-url]
[![codecov][codecov-image]][codecov-url]
<p align="center">
<img src="https://thumbs.gfycat.com/ComplexEminentEquine-size_restricted.gif" alt="drawing" width="800"/>
<img src="https://i.imgur.com/H5FA1Qy.gif" alt="drawing" width="800"/>
</p>
# Demo
## Browser
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
Here is an example of `docx` working in `Angular`:
* https://stackblitz.com/edit/angular-afvxtz
## Node
Press `endpoint` on the `RunKit` website:
![RunKit Instructions](https://user-images.githubusercontent.com/2917613/38582539-f84311b6-3d07-11e8-90db-5885ae02c3c4.png)
@ -33,9 +47,11 @@ Press `endpoint` on the `RunKit` website:
* https://runkit.com/dolanmiu/docx-demo5 - Images
* https://runkit.com/dolanmiu/docx-demo6 - Margins
* https://runkit.com/dolanmiu/docx-demo7 - Landscape
* https://runkit.com/dolanmiu/docx-demo8/1.0.1 - Header and Footer
* https://runkit.com/dolanmiu/docx-demo8 - Header and Footer
* https://runkit.com/dolanmiu/docx-demo10 - **My CV generated with docx**
More [here](https://docx.js.org/#/examples) and [here](https://github.com/dolanmiu/docx/tree/master/demo)
# How to use & Documentation
Please refer to the [documentation at https://docx.js.org/](https://docx.js.org/) for details on how to use this library, examples and much more!
@ -66,3 +82,5 @@ Made with 💖
[gitter-url]: https://gitter.im/docx-lib/Lobby
[pr-image]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg
[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

View File

@ -15,8 +15,12 @@ const footer = doc.createFooter();
footer.createParagraph("Footer on another page");
doc.addSection({
headerId: header.Header.ReferenceId,
footerId: footer.Footer.ReferenceId,
headers: {
default: header,
},
footers: {
default: footer,
},
pageNumberStart: 1,
pageNumberFormatType: PageNumberFormat.DECIMAL,
});
@ -24,8 +28,12 @@ doc.addSection({
doc.createParagraph("hello");
doc.addSection({
headerId: header.Header.ReferenceId,
footerId: footer.Footer.ReferenceId,
headers: {
default: header,
},
footers: {
default: footer,
},
pageNumberStart: 1,
pageNumberFormatType: PageNumberFormat.DECIMAL,
orientation: PageOrientation.LANDSCAPE,

View File

@ -8,11 +8,11 @@ const doc = new Document();
const paragraph = new Paragraph("Hello World");
doc.addParagraph(paragraph);
const image = Media.addImage(doc, "./demo/images/image1.jpeg");
const image2 = Media.addImage(doc, "./demo/images/dog.png");
const image3 = Media.addImage(doc, "./demo/images/cat.jpg");
const image4 = Media.addImage(doc, "./demo/images/parrots.bmp");
const image5 = Media.addImage(doc, "./demo/images/pizza.gif");
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
const image2 = Media.addImage(doc, fs.readFileSync("./demo/images/dog.png"));
const image3 = Media.addImage(doc, fs.readFileSync("./demo/images/cat.jpg"));
const image4 = Media.addImage(doc, fs.readFileSync("./demo/images/parrots.bmp"));
const image5 = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"));
const imageBase64Data = `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`;
const image6 = Media.addImage(doc, Buffer.from(imageBase64Data, "base64"), 100, 100);

View File

@ -8,7 +8,7 @@ const doc = new Document();
const table = doc.createTable(4, 4);
table.getCell(2, 2).addContent(new Paragraph("Hello"));
const image = Media.addImage(doc, "./demo/images/image1.jpeg");
const image = Media.addImage(doc, fs.readFileSync("./demo/images/image1.jpeg"));
table.getCell(1, 1).addContent(image.Paragraph);
const packer = new Packer();

33
demo/demo27.ts Normal file
View File

@ -0,0 +1,33 @@
import * as fs from "fs";
import { Document, Packer } from "../build";
const doc = new Document();
const myStyles = doc.Styles;
// The first argument is an ID you use to apply the style to paragraphs
// The second argument is a human-friendly name to show in the UI
myStyles.createParagraphStyle("myWonkyStyle", "My Wonky Style")
.basedOn("Normal")
.next("Normal")
.color("990000")
.italics()
.indent({left: 720}) // 720 TWIP === 720 / 20 pt === .5 in
.spacing({line: 276}); // 276 / 240 = 1.15x line spacing
myStyles.createParagraphStyle("Heading2", "Heading 2")
.basedOn("Normal")
.next("Normal")
.quickFormat()
.size(26) // 26 half-points === 13pt font
.bold()
.underline("double", "FF0000")
.spacing({before: 240, after: 120}); // TWIP for both
doc.createParagraph("Hello").style("myWonkyStyle");
doc.createParagraph("World").heading2(); // Uses the Heading2 style
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

43
demo/demo28.ts Normal file
View File

@ -0,0 +1,43 @@
// Creates two paragraphs, one with a border and one without
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { File, Packer, Paragraph, StyleLevel, TableOfContents } from "../build";
const doc = new File();
// The first argument is an ID you use to apply the style to paragraphs
// The second argument is a human-friendly name to show in the UI
doc.Styles.createParagraphStyle("MySpectacularStyle", "My Spectacular Style")
.basedOn("Heading1")
.next("Heading1")
.color("990000")
.italics();
// WordprocessingML docs for TableOfContents can be found here:
// http://officeopenxml.com/WPtableOfContents.php
// Let's define the properties for generate a TOC for heading 1-5 and MySpectacularStyle,
// making the entries be hyperlinks for the paragraph
const toc = new TableOfContents("Summary", {
hyperlink: true,
headingStyleRange: "1-5",
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)],
});
doc.addTableOfContents(toc);
doc.addParagraph(new Paragraph("Header #1").heading1().pageBreakBefore());
doc.addParagraph(new Paragraph("I'm a little text very nicely written.'"));
doc.addParagraph(new Paragraph("Header #2").heading1().pageBreakBefore());
doc.addParagraph(new Paragraph("I'm a other text very nicely written.'"));
doc.addParagraph(new Paragraph("Header #2.1").heading2());
doc.addParagraph(new Paragraph("I'm a another text very nicely written.'"));
doc.addParagraph(new Paragraph("My Spectacular Style #1").style("MySpectacularStyle").pageBreakBefore());
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

32
demo/demo29.ts Normal file
View File

@ -0,0 +1,32 @@
import * as fs from "fs";
import { Document, Indent, Numbering, Packer, Paragraph } from "../build";
const doc = new Document();
const numbering = new Numbering();
const abstractNum = numbering.createAbstractNumbering();
abstractNum.createLevel(0, "upperRoman", "%1", "start").addParagraphProperty(new Indent({ left: 720, hanging: 260 }));
const concrete = numbering.createConcreteNumbering(abstractNum);
const item1 = new Paragraph("line with contextual spacing");
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);
doc.addParagraph(item1);
doc.addParagraph(item2);
doc.addParagraph(item3);
doc.addParagraph(item4);
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

29
demo/demo30.ts Normal file
View File

@ -0,0 +1,29 @@
import * as fs from "fs";
import { Document, ImportDotx, Packer, Paragraph } from "../build";
const importDotx = new ImportDotx();
const filePath = "./demo/dotx/template.dotx";
fs.readFile(filePath, (err, data) => {
if (err) {
throw new Error(`Failed to read file ${filePath}.`);
}
importDotx.extract(data).then((templateDocument) => {
// This any needs fixing
const sectionProps = {
titlePage: templateDocument.titlePageIsDefined,
} as any;
const doc = new Document(undefined, sectionProps, {
template: templateDocument,
});
const paragraph = new Paragraph("Hello World");
doc.addParagraph(paragraph);
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});
});
});

26
demo/demo31.ts Normal file
View File

@ -0,0 +1,26 @@
// Example of how you would create a table and add data to it
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph, VerticalAlign } from "../build";
const doc = new Document();
const table = doc.createTable(2, 2);
table
.getCell(1, 1)
.addContent(new Paragraph("This text should be in the middle of the cell"))
.CellProperties.setVerticalAlign(VerticalAlign.CENTER);
table
.getCell(1, 0)
.addContent(
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(),
);
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

35
demo/demo32.ts Normal file
View File

@ -0,0 +1,35 @@
// Example of how you would create a table and add data to it
// 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();
let table = doc.createTable(2, 2);
table.getCell(0, 0).addContent(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.getRow(0).mergeCells(0, 2);
doc.createParagraph("Another table").heading2();
table = doc.createTable(2, 4);
table.getCell(0, 0).addContent(new Paragraph("Foo"));
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.getRow(0).mergeCells(0, 3);
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

22
demo/demo33.ts Normal file
View File

@ -0,0 +1,22 @@
// Sequential Captions
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, Paragraph, TextRun } from "../build";
const doc = new Document();
const paragraph = new Paragraph("Hello World 1->").addSequentialIdentifier("Caption").addRun(new TextRun(" text after sequencial caption 2->")).addSequentialIdentifier("Caption");
const paragraph2 = new Paragraph("Hello World 1->").addSequentialIdentifier("Label").addRun(new TextRun(" text after sequencial caption 2->")).addSequentialIdentifier("Label");
const paragraph3 = new Paragraph("Hello World 1->").addSequentialIdentifier("Another").addRun(new TextRun(" text after sequencial caption 3->")).addSequentialIdentifier("Label");
const paragraph4 = new Paragraph("Hello World 2->").addSequentialIdentifier("Another").addRun(new TextRun(" text after sequencial caption 4->")).addSequentialIdentifier("Label");
doc.addParagraph(paragraph);
doc.addParagraph(paragraph2);
doc.addParagraph(paragraph3);
doc.addParagraph(paragraph4);
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

32
demo/demo34.ts Normal file
View File

@ -0,0 +1,32 @@
// 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";
const doc = new Document();
const table = doc.createTable(2, 2).float({
horizontalAnchor: TableAnchorType.MARGIN,
verticalAnchor: TableAnchorType.MARGIN,
relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT,
relativeVerticalPosition: RelativeVerticalPosition.BOTTOM,
});
table.setFixedWidthLayout();
table.setWidth(WidthType.DXA, 4535);
table.getCell(0, 0).addContent(new Paragraph("Hello"));
table.getRow(0).mergeCells(0, 1);
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

44
demo/demo38.ts Normal file
View File

@ -0,0 +1,44 @@
// Example of how to add images to the document - You can use Buffers, UInt8Arrays or Base64 strings
// 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, TextWrappingSide, TextWrappingType } from "../build";
const doc = new Document();
doc.createParagraph(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vehicula nec nulla vitae efficitur. Ut interdum mauris eu ipsum rhoncus, nec pharetra velit placerat. Sed vehicula libero ac urna molestie, id pharetra est pellentesque. Praesent iaculis vehicula fringilla. Duis pretium gravida orci eu vestibulum. Mauris tincidunt ipsum dolor, ut ornare dolor pellentesque id. Integer in nulla gravida, lacinia ante non, commodo ex. Vivamus vulputate nisl id lectus finibus vulputate. Ut et nisl mi. Cras fermentum augue arcu, ac accumsan elit euismod id. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed ac posuere nisi. Pellentesque tincidunt vehicula bibendum. Phasellus eleifend viverra nisl.",
);
doc.createParagraph(
"Proin ac purus faucibus, porttitor magna ut, cursus nisl. Vivamus ante purus, porta accumsan nibh eget, eleifend dignissim odio. Integer sed dictum est, aliquam lacinia justo. Donec ultrices auctor venenatis. Etiam interdum et elit nec elementum. Pellentesque nec viverra mauris. Etiam suscipit leo nec velit fringilla mattis. Pellentesque justo lacus, sodales eu condimentum in, dapibus finibus lacus. Morbi vitae nibh sit amet sem molestie feugiat. In non porttitor enim.",
);
doc.createParagraph(
"Ut eget diam cursus quam accumsan interdum at id ante. Ut mollis mollis arcu, eu scelerisque dui tempus in. Quisque aliquam, augue quis ornare aliquam, ex purus ultrices mauris, ut porta dolor dolor nec justo. Nunc a tempus odio, eu viverra arcu. Suspendisse vitae nibh nec mi pharetra tempus. Mauris ut ullamcorper sapien, et sagittis sapien. Vestibulum in urna metus. In scelerisque, massa id bibendum tempus, quam orci rutrum turpis, a feugiat nisi ligula id metus. Praesent id dictum purus. Proin interdum ipsum nulla.",
);
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
floating: {
horizontalPosition: {
offset: 2014400,
},
verticalPosition: {
offset: 2014400,
},
wrap: {
type: TextWrappingType.SQUARE,
side: TextWrappingSide.BOTH_SIDES,
},
margins: {
top: 201440,
bottom: 201440,
},
},
});
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

33
demo/demo39.ts Normal file
View File

@ -0,0 +1,33 @@
// Scaling images
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, Packer, PageNumberFormat, TextRun } from "../build";
const doc = new Document(
{},
{
pageNumberStart: 1,
pageNumberFormatType: PageNumberFormat.DECIMAL,
},
);
doc.Header.createParagraph("Foo Bar corp. ")
.addRun(new TextRun("Page Number ").pageNumber())
.addRun(new TextRun(" to ").numberOfTotalPages());
doc.Footer.createParagraph("Foo Bar corp. ")
.center()
.addRun(new TextRun("Page Number: ").pageNumber())
.addRun(new TextRun(" to ").numberOfTotalPages());
doc.createParagraph("Hello World 1").pageBreak();
doc.createParagraph("Hello World 2").pageBreak();
doc.createParagraph("Hello World 3").pageBreak();
doc.createParagraph("Hello World 4").pageBreak();
doc.createParagraph("Hello World 5").pageBreak();
const packer = new Packer();
packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

View File

@ -1,7 +1,8 @@
// Example of how to add images to the document - You can use Buffers, UInt8Arrays or Base64 strings
// 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 } from "../build";
import { Document, HorizontalPositionAlign, HorizontalPositionRelativeFrom, Packer, Paragraph, VerticalPositionAlign, VerticalPositionRelativeFrom} from "../build";
const doc = new Document();
@ -13,6 +14,29 @@ doc.createImage(fs.readFileSync("./demo/images/dog.png").toString("base64"));
doc.createImage(fs.readFileSync("./demo/images/cat.jpg"));
doc.createImage(fs.readFileSync("./demo/images/parrots.bmp"));
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"));
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
floating: {
horizontalPosition: {
offset: 1014400,
},
verticalPosition: {
offset: 1014400,
},
},
});
doc.createImage(fs.readFileSync("./demo/images/cat.jpg"), 200, 200, {
floating: {
horizontalPosition: {
relative: HorizontalPositionRelativeFrom.PAGE,
align: HorizontalPositionAlign.RIGHT,
},
verticalPosition: {
relative: VerticalPositionRelativeFrom.PAGE,
align: VerticalPositionAlign.BOTTOM,
},
},
});
const packer = new Packer();

BIN
demo/dotx/template.dotx Normal file

Binary file not shown.

View File

@ -1,5 +1,5 @@
<p align="center">
<img alt="clippy the assistant" src="http://i60.tinypic.com/339pvtt.png">
<img alt="clippy the assistant" src="https://i.imgur.com/pwCV6L8.png">
</p>
<p align="center">
@ -54,6 +54,10 @@ exporter.pack("My First Document");
[@h4buli](https://github.com/h4buli)
<p align="center">
<img alt="clippy the assistant" src="http://i60.tinypic.com/339pvtt.png">
</p>
---
Made with 💖

View File

@ -16,6 +16,8 @@
* [Bullet Points](usage/bullet-points.md)
* [Numbering](usage/numbering.md)
* [Tab Stops](usage/tab-stops.md)
* [Table of Contents](usage/table-of-contents.md)
* [Page Numbers](usage/page-numbers.md)
* Styling
* [Styling with JS](usage/styling-with-js.md)
* [Styling with XML](usage/styling-with-xml.md)

View File

@ -1,7 +1,5 @@
# Contribution Guidelines
## Writing Code
* Include documentation reference(s) at the top of each file:
```js
@ -12,6 +10,59 @@
* Follow the `TSLint` rules
## Always think about the user
The number one pillar for contribution is to **ALWAYS** think about how the user will use the library.
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?
4. Is it consistent with the rest of the API?
5. Is it fun to use?
## Good Commit Names
Please write good commit messages when making a commit: https://chris.beams.io/posts/git-commit/
**Do not:**
```
c // What?
rtl // Adding acryonyms without explaining anything else is not helpful
works! // Glad its working, but the message is not helpful
demo updated // Getting better, but capitalize the first letter
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.
Try to make method parameters accept primatives, 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:
```js
import { ChildComponent } from "./my-feature/sub-component/deeper/.../my-deep.component";
```
2. This is what I consider "leakage". The code is aware of the underlying implementation of the component.
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
```js
public float(tableFloatProperties: TableFloatProperties): Table
```
**Do**
`ITableFloatOptions` is an interface for a JSON of primatives.
```js
public float(tableFloatOptions: ITableFloatOptions): Table
```
## Add vs Create
This is just a guideline, and the rules can sometimes be broken.
@ -39,7 +90,7 @@ Getters and Setters are done with a capital letter like so:
```js
public get Level() {
...
}
```
@ -57,6 +108,86 @@ private get _level: string;
private get level: string;
```
## Interfaces over type alias
Do not use `type`, but rather use `Interfaces`. `type` cannot be extended, and a class cannot implement it.
> "In general, use what you want ( type alias / interface ) just be consistent"
> "always use interface for public API's definition when authoring a library or 3rd party ambient type definitions"
>
> * https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c
`Interface` is generally preferred over `type`: https://stackoverflow.com/questions/37233735/typescript-interfaces-vs-types
**Do not:**
```js
type RelationshipFileInfo = { id: number, target: string };
```
**Do:**
```js
interface IRelationshipFileInfo {
id: number;
target: string;
}
```
## String enums vs type
To take full advantage of TypeScript's typing system, its best to use `string enums`:
**Do not:**
```js
type WeaponType = "bow" | "sword" | "wand";
```
**Do:**
```js
enum WeaponType = {
BOW = "bow",
SWORD = "sword",
WAND = "wand",
}
```
## Spell correctly, 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:
**Do not:**
```js
readdy // misspelling
perm // abbreviation
conf // abbreviation
cnty // abbreviation
relationFile // abbreviation
colour // U.K. English
```
**Do:**
```js
ready
permission
config
country
relationshipFile
color
```
## Keep files small (within reason)
To minimize merge conflicts, reduce complexity, and improve readability, keep the files small.
## Name files and folders with `/foo-bar/kebab-case.ts`
To be consistent and in-line with the project, name files `like-this.ts`.
https://stackoverflow.com/questions/7273316/what-is-the-javascript-filename-naming-convention
## Testing
Please write a test of every file you make and suffix it with `.spec.ts`.
@ -78,3 +209,5 @@ describe("ClassName", () => {
});
});
```
Try not to use the `tests/utility.ts` file as this is being deprecated.

View File

@ -1,156 +1,231 @@
# Images
## Intro
To create a `floating` image on top of text:
Adding images is very simple
Simply call the `createImage` method:
```js
const image = doc.createImage([BUFFER_OF_YOUR_IMAGE]);
```
`docx` supports `jpeg`, `jpg`, `bmp`, `gif` and `png`
Check `demo5.js` for an example
## Positioning
Images can be:
* floating position of images
* Wrapped around the text
* Inline
By default, picture are exported as `INLINE` elements.
In Word this is found in:
![Word Image Positiong](https://user-images.githubusercontent.com/34742290/41765548-b0946302-7604-11e8-96f9-166a9f0b8f39.png)
### Usage
The `PictureRun` element support various options to define the positioning of the element in the document.
```js
interface DrawingOptions {
position?: PlacementPosition;
textWrapping?: TextWrapping;
floating?: Floating;
}
```
can be passed when creating `PictureRun()` for example:
```js
const imageData = document.createImage(buffer, 903, 1149);
new docx.PictureRun(imageData, {
position: docx.PlacementPosition.FLOATING,
```ts
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
floating: {
horizontalPosition: {
relative: HorizontalPositionRelativeFrom.PAGE,
align: HorizontalPositionAlign.LEFT,
offset: 1014400,
},
verticalPosition: {
relative: VerticalPositionRelativeFrom.PAGE,
align: VerticalPositionAlign.TOP,
offset: 1014400,
},
},
});
```
So, the first thing is to define the placement position: `INLINE` or `FLOATING`. Inline is the default one so there is no need to pass drawing options for inline.
By default with no arguments, its an `inline` image:
When placement position is FLOATING then we can use two options:
### Wrap text
for `drawingOptions.textWrapping` we can define various options. `textWrapping` has the following properties:
```js
interface TextWrapping {
textWrapStyle: TextWrapStyle;
wrapTextOption?: WrapTextOption;
distanceFromText?: Distance;
}
enum TextWrapStyle {
NONE,
SQUARE,
TIGHT,
TOP_AND_BOTTOM,
}
enum WrapTextOption {
BOTH_SIDES = "bothSides",
LEFT = "left",
RIGHT = "right",
LARGEST = "largest",
}
```ts
doc.createImage(fs.readFileSync("./demo/images/parrots.bmp"));
```
### Floating position
You can also create images manually and add them later:
When we want to position the image relative or absolute then we need to use option `drawingOptions.floating`:
```js
export interface Floating {
horizontalPosition: HorizontalPositionOptions;
verticalPosition: VerticalPositionOptions;
allowOverlap?: boolean;
lockAnchor?: boolean;
behindDocument?: boolean;
layoutInCell?: boolean;
}
export interface HorizontalPositionOptions {
relative: HorizontalPositionRelativeFrom;
align?: HorizontalPositionAlign;
offset?: number;
}
export interface VerticalPositionOptions {
relative: VerticalPositionRelativeFrom;
align?: VerticalPositionAlign;
offset?: number;
}
export enum HorizontalPositionRelativeFrom {
CHARACTER = "character",
COLUMN = "column",
INSIDE_MARGIN = "insideMargin",
LEFT_MARGIN = "leftMargin",
MARGIN = "margin",
OUTSIDE_MARGIN = "outsideMargin",
PAGE = "page",
RIGHT_MARGIN = "rightMargin",
}
export enum VerticalPositionRelativeFrom {
BOTTOM_MARGIN = "bottomMargin",
INSIDE_MARGIN = "insideMargin",
LINE = "line",
MARGIN = "margin",
OUTSIDE_MARGIN = "outsideMargin",
PAGE = "page",
PARAGRAPH = "paragraph",
TOP_MARGIN = "topMargin",
}
export enum HorizontalPositionAlign {
CENTER = "center",
INSIDE = "inside",
LEFT = "left",
OUTSIDE = "outside",
RIGHT = "right",
}
export enum VerticalPositionAlign {
BOTTOM = "bottom",
CENTER = "center",
INSIDE = "inside",
OUTSIDE = "outside",
TOP = "top",
}
```ts
const image = Media.addImage(doc, fs.readFileSync("./demo/images/pizza.gif"));
doc.addImage(image);
```
## Intro
Adding images can be done in two ways:
1. Call the `createImage` method to add the image directly into the `document`:
```js
doc.createImage([IMAGE_BUFFER], [WIDTH], [HEIGHT], [POSITION_OPTIONS]);
```
2. Create an `image` first, then add it into the `document`:
```ts
const image = Media.addImage(doc, [IMAGE_BUFFER]);
doc.addImage(image);
```
`docx` supports `jpeg`, `jpg`, `bmp`, `gif` and `png`
## Positioning
> Positioning is the method on how to place the image on the document
![Word Image Positiong](https://user-images.githubusercontent.com/34742290/41765548-b0946302-7604-11e8-96f9-166a9f0b8f39.png)
Three types of image positioning is supported:
- Floating
- Inline
By default, picture are exported as `Inline` elements.
### Usage
Pass `options` into the `[POSITION_OPTIONS]` metioned in the [Intro above](#Intro).
## Floating
To change the position the image to be on top of the text, simply add the `floating` property to the last argument. By default, the offsets are relative to the top left corner of the `page`. Offset units are in [emus](https://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/):
```ts
const imageData = document.createImage(buffer, 903, 1149, {
floating: {
horizontalPosition: {
offset: 1014400, // relative: HorizontalPositionRelativeFrom.PAGE by default
},
verticalPosition: {
offset: 1014400, // relative: VerticalPositionRelativeFrom.PAGE by default
},
},
});
```
```ts
const imageData = document.createImage(buffer, 903, 1149, {
floating: {
horizontalPosition: {
relative: HorizontalPositionRelativeFrom.RIGHT_MARGIN,
offset: 1014400,
},
verticalPosition: {
relative: VerticalPositionRelativeFrom.BOTTOM_MARGIN,
offset: 1014400,
},
},
});
```
### Options
Full options you can pass into `floating` are:
| Property | Type | Notes |
| ------------------ | --------------------------- | -------- |
| horizontalPosition | `HorizontalPositionOptions` | Required |
| verticalPosition | `VerticalPositionOptions` | Required |
| allowOverlap | `boolean` | Optional |
| lockAnchor | `boolean` | Optional |
| behindDocument | `boolean` | Optional |
| layoutInCell | `boolean` | Optional |
`HorizontalPositionOptions` are:
| Property | Type | Notes | Possible Values |
| -------- | -------------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| relative | `HorizontalPositionRelativeFrom` | Required | `CHARACTER`, `COLUMN`, `INSIDE_MARGIN`, `LEFT_MARGIN`, `MARGIN`, `OUTSIDE_MARGIN`, `PAGE`, `RIGHT_MARGIN` |
| align | `HorizontalPositionAlign` | You can either have `align` or `offset`, not both | `CENTER`, `INSIDE`, `LEFT`, `OUTSIDE`, `RIGHT` |
| offset | `number` | You can either have `align` or `offset`, not both | `0` to `Infinity` |
`VerticalPositionOptions` are:
| Property | Type | Notes | Possible Values |
| -------- | ------------------------------ | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| relative | `VerticalPositionRelativeFrom` | Required | `BOTTOM_MARGIN`, `INSIDE_MARGIN`, `LINE`, `MARGIN`, `OUTSIDE_MARGIN`, `PAGE`, `PARAGRAPH`, `TOP_MARGIN` |
| align | `VerticalPositionAlign` | You can either have `align` or `offset`, not both | `BOTTOM`, `CENTER`, `INSIDE`, `OUTSIDE`, `TOP` |
| offset | `number` | You can either have `align` or `offset`, not both | `0` to `Infinity` |
## Wrap text
Wrapping only works for floating elements. Text will "wrap" around the floating `image`.
Add `wrap` options inside the `floating` options:
```ts
wrap: {
type: [TextWrappingType],
side: [TextWrappingSide],
},
```
For example:
```ts
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
floating: {
horizontalPosition: {
offset: 2014400,
},
verticalPosition: {
offset: 2014400,
},
wrap: {
type: TextWrappingType.SQUARE,
side: TextWrappingSide.BOTH_SIDES,
},
},
});
```
Wrap options have the following properties are:
| Property | Type | Notes | Possible Values |
| -------- | ------------------ | -------- | ------------------------------------------- |
| type | `TextWrappingType` | Optional | `NONE`, `SQUARE`, `TIGHT`, `TOP_AND_BOTTOM` |
| side | `TextWrappingSide` | Optional | `BOTH_SIDES`, `LEFT`, `RIGHT`, `LARGEST` |
## Margins
Margins give some space between the text and the image. Margins [only work for floating elements](http://officeopenxml.com/drwPicInline.php). Additionally, the image must also be in wrap mode (see above).
?> Be sure to also set `wrap` in your options!
To use, add the `margins` options inside the `floating` options:
```ts
margins: {
top: number,
bottom: number,
left: number,
right: number
},
```
For example:
```ts
doc.createImage(fs.readFileSync("./demo/images/pizza.gif"), 200, 200, {
floating: {
horizontalPosition: {
offset: 2014400,
},
verticalPosition: {
offset: 2014400,
},
wrap: {
type: TextWrappingType.SQUARE,
side: TextWrappingSide.BOTH_SIDES,
},
margins: {
top: 201440,
bottom: 201440,
},
},
});
```
## Examples
### Add image to the document
Importing Images from file system path
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo5.ts ":include")
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo5.ts_
### Add images to header and footer
Example showing how to add image to headers and footers
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo9.ts ":include")
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo9.ts_
### Floating images
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")
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo38.ts_

View File

@ -75,7 +75,7 @@ abstractNum.createLevel(0, "upperRoman", "%1", "start").addParagraphProperty(new
abstractNum.createLevel(1, "decimal", "%2.", "start").addParagraphProperty(new Indent(1440, 980));
abstractNum.createLevel(2, "lowerLetter", "%3)", "start").addParagraphProperty(new Indent(2160, 1700));
const concrete = numbering.createConcreteNumbering(numberedAbstract);
const concrete = numbering.createConcreteNumbering(abstractNum);
```
You can then apply your concrete style to paragraphs using their

View File

@ -0,0 +1,66 @@
# Page Numbers
> This feature allows you to set page numbers on each page
?> **Note:** This feature only works on Headers and Footers
```ts
doc.Header.createParagraph().addRun(new TextRun("Page Number: ").pageNumber()).addRun(new TextRun("to ").numberOfTotalPages());
```
## Current page number
To get the current page number, call the `.pageNumber()` method on a `TextRun`. Then add the newly created `TextRun` into a paragraph
```ts
pageNumber();
```
For example:
```ts
const currentPageRun = new TextRun("Current Page Number: ").pageNumber();
paragraph.addRun(currentPageRun);
```
## Total number of pages
```ts
numberOfTotalPages();
```
For example:
```ts
const lastPage = new TextRun("Total Page Number: ").numberOfTotalPages();
paragraph.addRun(lastPage);
```
## Both
You can combine the two to get "Page 2 of 10" effect:
```ts
const currentPageRun = new TextRun("Page ").pageNumber();
const lastPage = new TextRun("of ").numberOfTotalPages();
paragraph.addRun(currentPageRun);
paragraph.addRun(lastPage);
```
Or:
```ts
doc.Header.createParagraph().addRun(new TextRun("Page ").pageNumber()).addRun(new TextRun("of ").numberOfTotalPages());
```
## Examples
### Simple Example
Adding page numbers to Header and Footer
[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

@ -20,6 +20,7 @@ const name = new TextRun("Name:")
* `.size(halfPts)`: Set the font size, measured in half-points
* `.font(name)`: Set the run's font
* `.style(name)`: Apply a named run style
* `.characterSpacing(value)`: Set the character spacing adjustment (in TWIPs)
* For paragraph formatting:
* `.heading1()`, `.heading2()`, `.heading3()`, `.heading4()`, `.heading5()`, `.title()`: apply the appropriate style to the paragraph
* `.left()`, `.center()`, `.right()`, `.justified()`: set the paragraph's alignment

View File

@ -0,0 +1,76 @@
# Table of Contents
You can generate table of contents with `docx`. More information can be found [here](http://officeopenxml.com/WPtableOfContents.php).
>Tables of Contents are fields and, by design, it's content is only generated or updated by Word. We can't do it programatically.
>This is why, when you open a the file, Word you will prompt the message "This document contains fields that may refer to other files. Do you want to update the fields in this document?".
>You have say yes to Word generate the content of all table of contents.
The complete documentation can be found [here](https://www.ecma-international.org/publications/standards/Ecma-376.htm) (at Part 1, Page 1251).
## How to
All you need to do is create a `TableOfContents` object and assign it to the document.
```js
const toc = new TableOfContents("Summary", {
hyperlink: true,
headingStyleRange: "1-5",
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)]
});
doc.addTableOfContents(toc);
```
## Table of Contents Options
Here is the list of all options that you can use to generate your tables of contents:
| Option | Type | TOC Field Switch | Description |
| --- | --- | --- | --- |
|captionLabel|string|`\a`|Includes captioned items, but omits caption labels and numbers. The identifier designated by `text` in this switch's field-argument corresponds to the caption label. Use ``\c`` to build a table of captions with labels and numbers.|
|entriesFromBookmark|string|`\b`|Includes entries only from the portion of the document marked by the bookmark named by `text` in this switch's field-argument.|
|captionLabelIncludingNumbers|string|`\c`|Includes figures, tables, charts, and other items that are numbered by a SEQ field (§17.16.5.56). The sequence identifier designated by `text` in this switch's field-argument, which corresponds to the caption label, shall match the identifier in the corresponding SEQ field.|
|sequenceAndPageNumbersSeparator|string|`\d`|When used with `\s`, the `text` in this switch's field-argument defines the separator between sequence and page numbers. The default separator is a hyphen (-).|
|tcFieldIdentifier|string|`\f`|Includes only those TC fields whose identifier exactly matches the `text` in this switch's field-argument (which is typically a letter).|
|hyperlink|boolean|`\h`|Makes the table of contents entries hyperlinks.|
|tcFieldLevelRange|string|`\l`|Includes TC fields that assign entries to one of the levels specified by `text` in this switch's field-argument as a range having the form startLevel-endLevel, where startLevel and endLevel are integers, and startLevel has a value equal-to or less-than endLevel. TC fields that assign entries to lower levels are skipped.|
|pageNumbersEntryLevelsRange|string|`\n`|Without field-argument, omits page numbers from the table of contents. Page numbers are omitted from all levels unless a range of entry levels is specified by `text` in this switch's field-argument. A range is specified as for `\l`.|
|headingStyleRange|string|`\o`|Uses paragraphs formatted with all or the specified range of builtin heading styles. Headings in a style range are specified by `text` in this switch's field-argument using the notation specified as for `\l`, where each integer corresponds to the style with a style ID of HeadingX (e.g. 1 corresponds to Heading1). If no heading range is specified, all heading levels used in the document are listed.|
|entryAndPageNumberSeparator|string|`\p`|`text` in this switch's field-argument specifies a sequence of characters that separate an entry and its page number. The default is a tab with leader dots.|
|seqFieldIdentifierForPrefix|string|`\s`|For entries numbered with a SEQ field (§17.16.5.56), adds a prefix to the page number. The prefix depends on the type of entry. `text` in this switch's field-argument shall match the identifier in the SEQ field.|
|stylesWithLevels|StyleLevel[]|`\t`| Uses paragraphs formatted with styles other than the built-in heading styles. `text` in this switch's field-argument specifies those styles as a set of comma-separated doublets, with each doublet being a comma-separated set of style name and table of content level. `\t` can be combined with `\o`.|
|useAppliedParagraphOutlineLevel|boolean|`\u`|Uses the applied paragraph outline level.|
|preserveTabInEntries|boolean|`\w`|Preserves tab entries within table entries.|
|preserveNewLineInEntries|boolean|`\x`|Preserves newline characters within table entries.|
|hideTabAndPageNumbersInWebView|boolean|`\z`|Hides tab leader and page numbers in web page view (§17.18.102).|
## Examples
```js
// Let's define the options for generate a TOC for heading 1-5 and MySpectacularStyle,
// making the entries be hyperlinks for the paragraph
const toc = new TableOfContents("Summary", {
hyperlink: true,
headingStyleRange: "1-5",
stylesWithLevels: [new StyleLevel("MySpectacularStyle", 1)]
});
doc.addTableOfContents(toc);
doc.addParagraph(new Paragraph("Header #1").heading1().pageBreakBefore());
doc.addParagraph(new Paragraph("I'm a little text, very nicely written.'"));
doc.addParagraph(new Paragraph("Header #2").heading1().pageBreakBefore());
doc.addParagraph(new Paragraph("I'm another text very nicely written.'"));
doc.addParagraph(new Paragraph("Header #2.1").heading2());
doc.addParagraph(new Paragraph("I'm another text very nicely written.'"));
doc.addParagraph(new Paragraph("My Spectacular Style #1").style("MySpectacularStyle").pageBreakBefore());
```
### Complete example
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/demo28.ts ":include")
_Source: https://github.com/dolanmiu/docx/blob/master/demo/demo28.ts_

BIN
logo/logo-small.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
logo/logo-small.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
logo/logo-small.psd Normal file

Binary file not shown.

BIN
logo/logo.psd Normal file

Binary file not shown.

View File

@ -1,22 +1,22 @@
{
"name": "docx",
"version": "4.0.0",
"version": "4.7.0",
"description": "Generate .docx documents with JavaScript (formerly Office-Clippy)",
"main": "build/index.js",
"scripts": {
"pretest": "rimraf ./build",
"test": "mocha-webpack \"src/**/*.ts\"",
"test-watch": "mocha-webpack \"src/**/*.ts\" --watch",
"test.coverage": "nyc npm test",
"test.watch": "npm test -- --watch",
"prepublishOnly": "npm run build",
"lint": "tslint --project .",
"build": "npm run webpack && npm run fix-types",
"tsc": "rimraf ./build && tsc -p .",
"webpack": "rimraf ./build && webpack",
"build.web": "webpack --config webpack.web.config.js",
"demo": "npm run build && npm run ts-node ./demo",
"typedoc": "typedoc src/index.ts",
"style": "prettier -l \"src/**/*.ts\"",
"style.fix": "prettier \"src/**/*.ts\" --write",
"style.fix": "npm run style -- --write",
"fix-types": "node types-absolute-fixer.js",
"ts-node": "ts-node"
},
@ -48,11 +48,11 @@
"types": "./build/index.d.ts",
"dependencies": {
"@types/image-size": "0.0.29",
"@types/jszip": "^3.1.3",
"fast-xml-parser": "^3.3.6",
"@types/jszip": "^3.1.4",
"image-size": "^0.6.2",
"jszip": "^3.1.5",
"xml": "^1.0.1"
"xml": "^1.0.1",
"xml-js": "^1.6.8"
},
"author": "Dolan Miu",
"license": "MIT",
@ -67,9 +67,11 @@
"awesome-typescript-loader": "^3.4.1",
"chai": "^3.5.0",
"glob": "^7.1.2",
"istanbul-instrumenter-loader": "^3.0.1",
"jszip": "^3.1.5",
"mocha": "^5.2.0",
"mocha-webpack": "^1.0.1",
"nyc": "^13.1.0",
"pre-commit": "^1.2.2",
"prettier": "^1.12.1",
"prompt": "^1.0.0",
@ -79,8 +81,12 @@
"sinon": "^5.0.7",
"ts-node": "^7.0.1",
"tslint": "^5.11.0",
"tslint-immutable": "^4.9.0",
"typedoc": "^0.11.1",
"typescript": "2.9.2",
"webpack": "^3.10.0"
},
"engines": {
"node": ">=8"
}
}

View File

@ -2,6 +2,12 @@ import { BaseXmlComponent, IXmlableObject } from "file/xml-components";
export class Formatter {
public format(input: BaseXmlComponent): IXmlableObject {
return input.prepForXml();
const output = input.prepForXml();
if (output) {
return output;
} else {
throw Error("XMLComponent did not format correctly");
}
}
}

View File

@ -1,6 +1,8 @@
/* tslint:disable:typedef space-before-function-paren */
import { expect } from "chai";
import { File } from "../../file";
import { File } from "file";
import { Compiler } from "./next-compiler";
describe("Compiler", () => {
@ -19,7 +21,7 @@ describe("Compiler", () => {
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
expect(fileNames).is.an.instanceof(Array);
expect(fileNames).has.length(17);
expect(fileNames).has.length(18);
expect(fileNames).to.include("word/document.xml");
expect(fileNames).to.include("word/styles.xml");
expect(fileNames).to.include("docProps/core.xml");
@ -29,6 +31,7 @@ describe("Compiler", () => {
expect(fileNames).to.include("word/_rels/header1.xml.rels");
expect(fileNames).to.include("word/footer1.xml");
expect(fileNames).to.include("word/footnotes.xml");
expect(fileNames).to.include("word/settings.xml");
expect(fileNames).to.include("word/_rels/footer1.xml.rels");
expect(fileNames).to.include("word/_rels/document.xml.rels");
expect(fileNames).to.include("[Content_Types].xml");
@ -47,7 +50,7 @@ describe("Compiler", () => {
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
expect(fileNames).is.an.instanceof(Array);
expect(fileNames).has.length(25);
expect(fileNames).has.length(26);
expect(fileNames).to.include("word/header1.xml");
expect(fileNames).to.include("word/_rels/header1.xml.rels");

View File

@ -5,24 +5,25 @@ import { File } from "file";
import { Formatter } from "../formatter";
interface IXmlifyedFile {
data: string;
path: string;
readonly data: string;
readonly path: string;
}
interface IXmlifyedFileMapping {
Document: IXmlifyedFile;
Styles: IXmlifyedFile;
Properties: IXmlifyedFile;
Numbering: IXmlifyedFile;
Relationships: IXmlifyedFile;
FileRelationships: IXmlifyedFile;
Headers: IXmlifyedFile[];
Footers: IXmlifyedFile[];
HeaderRelationships: IXmlifyedFile[];
FooterRelationships: IXmlifyedFile[];
ContentTypes: IXmlifyedFile;
AppProperties: IXmlifyedFile;
FootNotes: IXmlifyedFile;
readonly Document: IXmlifyedFile;
readonly Styles: IXmlifyedFile;
readonly Properties: IXmlifyedFile;
readonly Numbering: IXmlifyedFile;
readonly Relationships: IXmlifyedFile;
readonly FileRelationships: IXmlifyedFile;
readonly Headers: IXmlifyedFile[];
readonly Footers: IXmlifyedFile[];
readonly HeaderRelationships: IXmlifyedFile[];
readonly FooterRelationships: IXmlifyedFile[];
readonly ContentTypes: IXmlifyedFile;
readonly AppProperties: IXmlifyedFile;
readonly FootNotes: IXmlifyedFile;
readonly Settings: IXmlifyedFile;
}
export class Compiler {
@ -58,10 +59,23 @@ 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();
return {
Document: {
data: xml(this.formatter.format(file.Document), true),
@ -120,6 +134,19 @@ export class Compiler {
data: xml(this.formatter.format(file.FootNotes)),
path: "word/footnotes.xml",
},
Settings: {
data: xml(this.formatter.format(file.Settings)),
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

@ -2,7 +2,8 @@
import { assert } from "chai";
import { stub } from "sinon";
import { File, Paragraph } from "../../file";
import { File, Paragraph } from "file";
import { Packer } from "./packer";
describe("Packer", () => {

View File

@ -10,21 +10,30 @@ export class Packer {
public async toBuffer(file: File): Promise<Buffer> {
const zip = await this.compiler.compile(file);
const zipData = (await zip.generateAsync({ type: "nodebuffer" })) as Buffer;
const zipData = (await zip.generateAsync({
type: "nodebuffer",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
})) as Buffer;
return zipData;
}
public async toBase64String(file: File): Promise<string> {
const zip = await this.compiler.compile(file);
const zipData = (await zip.generateAsync({ type: "base64" })) as string;
const zipData = (await zip.generateAsync({
type: "base64",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
})) as string;
return zipData;
}
public async toBlob(file: File): Promise<Blob> {
const zip = await this.compiler.compile(file);
const zipData = (await zip.generateAsync({ type: "blob" })) as Blob;
const zipData = (await zip.generateAsync({
type: "blob",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
})) as Blob;
return zipData;
}

View File

@ -1,12 +1,12 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IAppPropertiesAttributes {
xmlns: string;
vt: string;
readonly xmlns: string;
readonly vt: string;
}
export class AppPropertiesAttributes extends XmlAttributeComponent<IAppPropertiesAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
xmlns: "xmlns",
vt: "xmlns:vt",
};

View File

@ -1,11 +1,11 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IContentTypeAttributes {
xmlns?: string;
readonly xmlns?: string;
}
export class ContentTypeAttributes extends XmlAttributeComponent<IContentTypeAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
xmlns: "xmlns",
};
}

View File

@ -1,7 +1,9 @@
// tslint:disable:no-string-literal
import { expect } from "chai";
import { Formatter } from "../../export/formatter";
import { Formatter } from "export/formatter";
import { ContentTypes } from "./content-types";
describe("ContentTypes", () => {

View File

@ -1,12 +1,12 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IDefaultAttributes {
contentType: string;
extension?: string;
readonly contentType: string;
readonly extension?: string;
}
export class DefaultAttributes extends XmlAttributeComponent<IDefaultAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
contentType: "ContentType",
extension: "Extension",
};

View File

@ -1,12 +1,12 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IOverrideAttributes {
contentType: string;
partName?: string;
readonly contentType: string;
readonly partName?: string;
}
export class OverrideAttributes extends XmlAttributeComponent<IOverrideAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
contentType: "ContentType",
partName: "PartName",
};

View File

@ -1,6 +1,7 @@
import { expect } from "chai";
import { Formatter } from "../../export/formatter";
import { Formatter } from "export/formatter";
import { CoreProperties } from "./properties";
describe("Properties", () => {

View File

@ -3,14 +3,14 @@ import { DocumentAttributes } from "../document/document-attributes";
import { Created, Creator, Description, Keywords, LastModifiedBy, Modified, Revision, Subject, Title } from "./components";
export interface IPropertiesOptions {
title?: string;
subject?: string;
creator?: string;
keywords?: string;
description?: string;
lastModifiedBy?: string;
revision?: string;
externalStyles?: string;
readonly title?: string;
readonly subject?: string;
readonly creator?: string;
readonly keywords?: string;
readonly description?: string;
readonly lastModifiedBy?: string;
readonly revision?: string;
readonly externalStyles?: string;
}
export class CoreProperties extends XmlComponent {

View File

@ -1,6 +1,7 @@
import { expect } from "chai";
import { Formatter } from "../../../export/formatter";
import { Formatter } from "export/formatter";
import { Body } from "./body";
describe("Body", () => {
@ -16,11 +17,11 @@ describe("Body", () => {
expect(formatted)
.to.have.property("w:sectPr")
.and.to.be.an.instanceof(Array);
expect(formatted["w:sectPr"]).to.have.length(7);
expect(formatted["w:sectPr"]).to.have.length(5);
});
});
describe("addSection", () => {
describe("#addSection", () => {
it("should add section with options", () => {
body.addSection({
width: 10000,
@ -38,5 +39,93 @@ describe("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" } }] });
});
it("should add section with default parameters", () => {
body.addSection({
width: 10000,
height: 10000,
});
const tree = new Formatter().format(body);
expect(tree).to.deep.equal({
"w:body": [
{
"w:p": [
{ "w:pPr": [] },
{
"w:pPr": [
{
"w:sectPr": [
{ "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:cols": [{ _attr: { "w:space": 708 } }] },
{ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] },
{ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] },
],
},
],
},
],
},
{
"w:sectPr": [
{ "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:cols": [{ _attr: { "w:space": 708 } }] },
{ "w:docGrid": [{ _attr: { "w:linePitch": 360 } }] },
{ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] },
],
},
],
});
});
});
describe("#getParagraphs", () => {
it("should get no paragraphs", () => {
const paragraphs = body.getParagraphs();
expect(paragraphs).to.be.an.instanceof(Array);
});
});
describe("#DefaultSection", () => {
it("should get section", () => {
const section = body.DefaultSection;
const tree = new Formatter().format(section);
expect(tree["w:sectPr"]).to.be.an.instanceof(Array);
});
});
});

View File

@ -1,5 +1,5 @@
import { IXmlableObject, XmlComponent } from "file/xml-components";
import { Paragraph, ParagraphProperties } from "../..";
import { Paragraph, ParagraphProperties, TableOfContents } from "../..";
import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties";
export class Body extends XmlComponent {
@ -35,11 +35,9 @@ export class Body extends XmlComponent {
this.sections.push(new SectionProperties(params));
}
}
public prepForXml(): IXmlableObject {
public prepForXml(): IXmlableObject | undefined {
if (this.sections.length === 1) {
this.root.push(this.sections[0]);
} else if (this.sections.length > 1) {
throw new Error("Invalid usage of sections. At the end of the body element there must be ONE section.");
}
return super.prepForXml();
@ -53,6 +51,14 @@ export class Body extends XmlComponent {
return this.defaultSection;
}
public getTablesOfContents(): TableOfContents[] {
return this.root.filter((child) => child instanceof TableOfContents) as TableOfContents[];
}
public getParagraphs(): Paragraph[] {
return this.root.filter((child) => child instanceof Paragraph) as Paragraph[];
}
private createSectionParagraph(section: SectionProperties): Paragraph {
const paragraph = new Paragraph();
const properties = new ParagraphProperties();

View File

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

View File

@ -1,11 +1,11 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IDocGridAttributesProperties {
linePitch?: number;
readonly linePitch?: number;
}
export class DocGridAttributes extends XmlAttributeComponent<IDocGridAttributesProperties> {
protected xmlKeys = {
protected readonly xmlKeys = {
linePitch: "w:linePitch",
};
}

View File

@ -7,12 +7,12 @@ export enum FooterReferenceType {
}
export interface IFooterReferenceAttributes {
type: string;
id: string;
readonly type: string;
readonly id: string;
}
export class FooterReferenceAttributes extends XmlAttributeComponent<IFooterReferenceAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
type: "w:type",
id: "r:id",
};

View File

@ -2,8 +2,8 @@ import { XmlComponent } from "file/xml-components";
import { FooterReferenceAttributes, FooterReferenceType } from "./footer-reference-attributes";
export interface IFooterOptions {
footerType?: FooterReferenceType;
footerId?: number;
readonly footerType?: FooterReferenceType;
readonly footerId?: number;
}
export class FooterReference extends XmlComponent {

View File

@ -7,12 +7,12 @@ export enum HeaderReferenceType {
}
export interface IHeaderReferenceAttributes {
type: string;
id: string;
readonly type: string;
readonly id: string;
}
export class HeaderReferenceAttributes extends XmlAttributeComponent<IHeaderReferenceAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
type: "w:type",
id: "r:id",
};

View File

@ -2,8 +2,8 @@ import { XmlComponent } from "file/xml-components";
import { HeaderReferenceAttributes, HeaderReferenceType } from "./header-reference-attributes";
export interface IHeaderOptions {
headerType?: HeaderReferenceType;
headerId?: number;
readonly headerType?: HeaderReferenceType;
readonly headerId?: number;
}
export class HeaderReference extends XmlComponent {

View File

@ -1,16 +1,15 @@
import { expect } from "chai";
import { Formatter } from "../../../../../export/formatter";
import { BorderStyle } from "../../../../styles";
import { Formatter } from "export/formatter";
import { BorderStyle } from "file/styles";
import { PageBorderDisplay, PageBorders, PageBorderZOrder } from "./page-borders";
describe("PageBorders", () => {
describe("#constructor()", () => {
it("should create empty element when no options are passed", () => {
const properties = new PageBorders();
const tree = new Formatter().format(properties);
expect(tree).to.equal("");
expect(() => new Formatter().format(properties)).to.throw();
});
it("should create page borders with some configuration", () => {

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 { BorderStyle } from "../../../../styles";
export enum PageBorderDisplay {
ALL_PAGES = "allPages",
@ -19,28 +19,28 @@ export enum PageBorderZOrder {
}
export interface IPageBorderAttributes {
display?: PageBorderDisplay;
offsetFrom?: PageBorderOffsetFrom;
zOrder?: PageBorderZOrder;
readonly display?: PageBorderDisplay;
readonly offsetFrom?: PageBorderOffsetFrom;
readonly zOrder?: PageBorderZOrder;
}
export interface IPageBorderConfiguration {
style?: BorderStyle;
size?: number;
color?: string;
space?: number;
readonly style?: BorderStyle;
readonly size?: number;
readonly color?: string;
readonly space?: number;
}
export interface IPageBordersOptions {
pageBorders?: IPageBorderAttributes;
pageBorderTop?: IPageBorderConfiguration;
pageBorderRight?: IPageBorderConfiguration;
pageBorderBottom?: IPageBorderConfiguration;
pageBorderLeft?: IPageBorderConfiguration;
readonly pageBorders?: IPageBorderAttributes;
readonly pageBorderTop?: IPageBorderConfiguration;
readonly pageBorderRight?: IPageBorderConfiguration;
readonly pageBorderBottom?: IPageBorderConfiguration;
readonly pageBorderLeft?: IPageBorderConfiguration;
}
class PageBordeAttributes extends XmlAttributeComponent<IPageBorderConfiguration> {
protected xmlKeys = {
protected readonly xmlKeys = {
style: "w:val",
size: "w:size",
color: "w:color",
@ -57,7 +57,7 @@ class PageBorder extends XmlComponent {
}
class PageBordersAttributes extends XmlAttributeComponent<IPageBorderAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
display: "w:display",
offsetFrom: "w:offsetFrom",
zOrder: "w:zOrder",
@ -98,7 +98,9 @@ export class PageBorders extends XmlComponent {
}
}
public prepForXml(): IXmlableObject {
return this.root.length > 0 ? super.prepForXml() : "";
public prepForXml(): IXmlableObject | undefined {
if (this.root.length > 0) {
return super.prepForXml();
}
}
}

View File

@ -1,17 +1,18 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IPageMarginAttributes {
top?: number;
right?: number;
bottom?: number;
left?: number;
header?: number;
footer?: number;
gutter?: number;
readonly top?: number;
readonly right?: number;
readonly bottom?: number;
readonly left?: number;
readonly header?: number;
readonly footer?: number;
readonly gutter?: number;
readonly mirror?: boolean;
}
export class PageMarginAttributes extends XmlAttributeComponent<IPageMarginAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
top: "w:top",
right: "w:right",
bottom: "w:bottom",
@ -19,5 +20,6 @@ export class PageMarginAttributes extends XmlAttributeComponent<IPageMarginAttri
header: "w:header",
footer: "w:footer",
gutter: "w:gutter",
mirror: "w:mirrorMargins",
};
}

View File

@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components";
import { PageMarginAttributes } from "./page-margin-attributes";
export class PageMargin extends XmlComponent {
constructor(top: number, right: number, bottom: number, left: number, header: number, footer: number, gutter: number) {
constructor(top: number, right: number, bottom: number, left: number, header: number, footer: number, gutter: number, mirror: boolean) {
super("w:pgMar");
this.root.push(
new PageMarginAttributes({
@ -13,6 +13,7 @@ export class PageMargin extends XmlComponent {
header: header,
footer: footer,
gutter: gutter,
mirror: mirror,
}),
);
}

View File

@ -17,12 +17,12 @@ export enum PageNumberFormat {
}
export interface IPageNumberTypeAttributes {
pageNumberStart?: number;
pageNumberFormatType?: PageNumberFormat;
readonly pageNumberStart?: number;
readonly pageNumberFormatType?: PageNumberFormat;
}
export class PageNumberTypeAttributes extends XmlAttributeComponent<IPageNumberTypeAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
pageNumberStart: "w:start",
pageNumberFormatType: "w:fmt",
};

View File

@ -6,13 +6,13 @@ export enum PageOrientation {
}
export interface IPageSizeAttributes {
width?: number;
height?: number;
orientation?: PageOrientation;
readonly width?: number;
readonly height?: number;
readonly orientation?: PageOrientation;
}
export class PageSizeAttributes extends XmlAttributeComponent<IPageSizeAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
width: "w:w",
height: "w:h",
orientation: "w:orient",

View File

@ -1,6 +1,7 @@
import { expect } from "chai";
import { Formatter } from "../../../../../export/formatter";
import { Formatter } from "export/formatter";
import { PageSize } from "./page-size";
import { PageOrientation } from "./page-size-attributes";

View File

@ -1,12 +1,19 @@
import { expect } from "chai";
import { Formatter } from "../../../../export/formatter";
import { FooterReferenceType, PageBorderOffsetFrom, PageNumberFormat } from "./";
import { Formatter } from "export/formatter";
import { FooterWrapper } from "file/footer-wrapper";
import { HeaderWrapper } from "file/header-wrapper";
import { Media } from "file/media";
import { PageBorderOffsetFrom } from "./page-border";
import { PageNumberFormat } from "./page-number";
import { SectionProperties } from "./section-properties";
describe("SectionProperties", () => {
describe("#constructor()", () => {
it("should create section properties with options", () => {
const media = new Media();
const properties = new SectionProperties({
width: 11906,
height: 16838,
@ -17,11 +24,15 @@ describe("SectionProperties", () => {
header: 708,
footer: 708,
gutter: 0,
mirror: false,
space: 708,
linePitch: 360,
headerId: 100,
footerId: 200,
footerType: FooterReferenceType.EVEN,
headers: {
default: new HeaderWrapper(media, 100),
},
footers: {
even: new FooterWrapper(media, 200),
},
pageNumberStart: 10,
pageNumberFormatType: PageNumberFormat.CARDINAL_TEXT,
});
@ -40,6 +51,7 @@ describe("SectionProperties", () => {
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
},
],
@ -69,15 +81,14 @@ describe("SectionProperties", () => {
"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": "rId0", "w:type": "default" } }] });
expect(tree["w:sectPr"][5]).to.deep.equal({ "w:footerReference": [{ _attr: { "r:id": "rId0", "w:type": "default" } }] });
expect(tree["w:sectPr"][6]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] });
expect(tree["w:sectPr"][4]).to.deep.equal({ "w:pgNumType": [{ _attr: { "w:fmt": "decimal" } }] });
});
it("should create section properties with changed options", () => {
@ -99,6 +110,7 @@ describe("SectionProperties", () => {
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
},
],
@ -124,6 +136,7 @@ describe("SectionProperties", () => {
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
},
],
@ -150,6 +163,7 @@ describe("SectionProperties", () => {
"w:left": 1440,
"w:header": 708,
"w:gutter": 0,
"w:mirrorMargins": false,
},
},
],
@ -164,7 +178,8 @@ describe("SectionProperties", () => {
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
expect(tree["w:sectPr"][7]).to.deep.equal({
const pgBorders = tree["w:sectPr"].find((item) => item["w:pgBorders"] !== undefined);
expect(pgBorders).to.deep.equal({
"w:pgBorders": [{ _attr: { "w:offsetFrom": "page" } }],
});
});

View File

@ -1,113 +1,172 @@
// http://officeopenxml.com/WPsection.php
import { FooterWrapper } from "file/footer-wrapper";
import { HeaderWrapper } from "file/header-wrapper";
import { XmlComponent } from "file/xml-components";
import { FooterReferenceType, IPageBordersOptions, IPageNumberTypeAttributes, PageBorders, PageNumberFormat, PageNumberType } from "./";
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 { FooterReference, IFooterOptions } from "./footer-reference/footer-reference";
import { HeaderReference, IHeaderOptions } from "./header-reference/header-reference";
import { HeaderReferenceType } from "./header-reference/header-reference-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 { 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 { PageSize } from "./page-size/page-size";
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
import { TitlePage } from "./title-page/title-page";
export interface IHeaderFooterGroup<T> {
readonly default?: T;
readonly first?: T;
readonly even?: T;
}
interface IHeadersOptions {
readonly headers?: IHeaderFooterGroup<HeaderWrapper>;
}
interface IFootersOptions {
readonly footers?: IHeaderFooterGroup<FooterWrapper>;
}
interface ITitlePageOptions {
readonly titlePage?: boolean;
}
export type SectionPropertiesOptions = IPageSizeAttributes &
IPageMarginAttributes &
IColumnsAttributes &
IDocGridAttributesProperties &
IHeaderOptions &
IFooterOptions &
IHeadersOptions &
IFootersOptions &
IPageNumberTypeAttributes &
IPageBordersOptions;
IPageBordersOptions &
ITitlePageOptions;
export class SectionProperties extends XmlComponent {
private readonly options: SectionPropertiesOptions;
constructor(options?: SectionPropertiesOptions) {
constructor(options: SectionPropertiesOptions = {}) {
super("w:sectPr");
const defaultOptions = {
width: 11906,
height: 16838,
top: 1440,
right: 1440,
bottom: 1440,
left: 1440,
header: 708,
footer: 708,
gutter: 0,
space: 708,
linePitch: 360,
orientation: PageOrientation.PORTRAIT,
headerType: HeaderReferenceType.DEFAULT,
headerId: 0,
footerType: FooterReferenceType.DEFAULT,
footerId: 0,
pageNumberStart: undefined,
pageNumberFormatType: PageNumberFormat.DECIMAL,
pageBorders: undefined,
pageBorderTop: undefined,
pageBorderRight: undefined,
pageBorderBottom: undefined,
pageBorderLeft: undefined,
};
const {
width = 11906,
height = 16838,
top = 1440,
right = 1440,
bottom = 1440,
left = 1440,
header = 708,
footer = 708,
gutter = 0,
mirror = false,
space = 708,
linePitch = 360,
orientation = PageOrientation.PORTRAIT,
headers,
footers,
pageNumberFormatType = PageNumberFormat.DECIMAL,
pageNumberStart,
pageBorders,
pageBorderTop,
pageBorderRight,
pageBorderBottom,
pageBorderLeft,
titlePage = false,
} = options;
const mergedOptions = {
...defaultOptions,
...options,
};
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 DocumentGrid(linePitch));
this.root.push(new PageSize(mergedOptions.width, mergedOptions.height, mergedOptions.orientation));
this.root.push(
new PageMargin(
mergedOptions.top,
mergedOptions.right,
mergedOptions.bottom,
mergedOptions.left,
mergedOptions.header,
mergedOptions.footer,
mergedOptions.gutter,
),
);
this.root.push(new Columns(mergedOptions.space));
this.root.push(new DocumentGrid(mergedOptions.linePitch));
this.addHeaders(headers);
this.addFooters(footers);
this.root.push(
new HeaderReference({
headerType: mergedOptions.headerType,
headerId: mergedOptions.headerId,
}),
);
this.root.push(
new FooterReference({
footerType: mergedOptions.footerType,
footerId: mergedOptions.footerId,
}),
);
this.root.push(new PageNumberType(pageNumberStart, pageNumberFormatType));
this.root.push(new PageNumberType(mergedOptions.pageNumberStart, mergedOptions.pageNumberFormatType));
if (
mergedOptions.pageBorders ||
mergedOptions.pageBorderTop ||
mergedOptions.pageBorderRight ||
mergedOptions.pageBorderBottom ||
mergedOptions.pageBorderLeft
) {
if (pageBorders || pageBorderTop || pageBorderRight || pageBorderBottom || pageBorderLeft) {
this.root.push(
new PageBorders({
pageBorders: mergedOptions.pageBorders,
pageBorderTop: mergedOptions.pageBorderTop,
pageBorderRight: mergedOptions.pageBorderRight,
pageBorderBottom: mergedOptions.pageBorderBottom,
pageBorderLeft: mergedOptions.pageBorderLeft,
pageBorders: pageBorders,
pageBorderTop: pageBorderTop,
pageBorderRight: pageBorderRight,
pageBorderBottom: pageBorderBottom,
pageBorderLeft: pageBorderLeft,
}),
);
}
this.options = mergedOptions;
if (titlePage) {
this.root.push(new TitlePage());
}
}
private addHeaders(headers?: IHeaderFooterGroup<HeaderWrapper>): void {
if (headers) {
if (headers.default) {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.DEFAULT,
headerId: headers.default.Header.ReferenceId,
}),
);
}
if (headers.first) {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.FIRST,
headerId: headers.first.Header.ReferenceId,
}),
);
}
if (headers.even) {
this.root.push(
new HeaderReference({
headerType: HeaderReferenceType.EVEN,
headerId: headers.even.Header.ReferenceId,
}),
);
}
}
}
private addFooters(footers?: IHeaderFooterGroup<FooterWrapper>): void {
if (footers) {
if (footers.default) {
this.root.push(
new FooterReference({
footerType: FooterReferenceType.DEFAULT,
footerId: footers.default.Footer.ReferenceId,
}),
);
}
if (footers.first) {
this.root.push(
new FooterReference({
footerType: FooterReferenceType.FIRST,
footerId: footers.first.Footer.ReferenceId,
}),
);
}
if (footers.even) {
this.root.push(
new FooterReference({
footerType: FooterReferenceType.EVEN,
footerId: footers.even.Footer.ReferenceId,
}),
);
}
}
}
public get Options(): SectionPropertiesOptions {

View File

@ -1,11 +1,11 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IHeaderReferenceAttributes {
value: string;
readonly value: string;
}
export class TitlePageAttributes extends XmlAttributeComponent<IHeaderReferenceAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
value: "w:val",
};
}

View File

@ -1,6 +1,7 @@
import { expect } from "chai";
import { Formatter } from "../../../../../export/formatter";
import { Formatter } from "export/formatter";
import { TitlePage } from "./title-page";
describe("PageSize", () => {

View File

@ -1,33 +1,33 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IDocumentAttributesProperties {
wpc?: string;
mc?: string;
o?: string;
r?: string;
m?: string;
v?: string;
wp14?: string;
wp?: string;
w10?: string;
w?: string;
w14?: string;
w15?: string;
wpg?: string;
wpi?: string;
wne?: string;
wps?: string;
Ignorable?: string;
cp?: string;
dc?: string;
dcterms?: string;
dcmitype?: string;
xsi?: string;
type?: string;
readonly wpc?: string;
readonly mc?: string;
readonly o?: string;
readonly r?: string;
readonly m?: string;
readonly v?: string;
readonly wp14?: string;
readonly wp?: string;
readonly w10?: string;
readonly w?: string;
readonly w14?: string;
readonly w15?: string;
readonly wpg?: string;
readonly wpi?: string;
readonly wne?: string;
readonly wps?: string;
readonly Ignorable?: string;
readonly cp?: string;
readonly dc?: string;
readonly dcterms?: string;
readonly dcmitype?: string;
readonly xsi?: string;
readonly type?: string;
}
export class DocumentAttributes extends XmlAttributeComponent<IDocumentAttributesProperties> {
protected xmlKeys = {
protected readonly xmlKeys = {
wpc: "xmlns:wpc",
mc: "xmlns:mc",
o: "xmlns:o",

View File

@ -1,6 +1,7 @@
import { assert, expect } from "chai";
import { Formatter } from "../../export/formatter";
import { Formatter } from "export/formatter";
import { Paragraph } from "../paragraph";
import { Table } from "../table";
import { Document } from "./document";

View File

@ -2,6 +2,7 @@
import { XmlComponent } from "file/xml-components";
import { Paragraph } from "../paragraph";
import { Table } from "../table";
import { TableOfContents } from "../table-of-contents";
import { Body } from "./body";
import { SectionPropertiesOptions } from "./body/section-properties";
import { DocumentAttributes } from "./document-attributes";
@ -41,6 +42,11 @@ export class Document extends XmlComponent {
return this;
}
public addTableOfContents(toc: TableOfContents): Document {
this.body.push(toc);
return this;
}
public createParagraph(text?: string): Paragraph {
const para = new Paragraph(text);
this.addParagraph(para);
@ -60,4 +66,12 @@ export class Document extends XmlComponent {
public get Body(): Body {
return this.body;
}
public getTablesOfContents(): TableOfContents[] {
return this.body.getTablesOfContents();
}
public getParagraphs(): Paragraph[] {
return this.body.getParagraphs();
}
}

View File

@ -1,2 +1,3 @@
export * from "./document";
export * from "./document-attributes";
export * from "./body";

View File

@ -2,16 +2,16 @@ import { XmlAttributeComponent } from "file/xml-components";
import { IDistance } from "../drawing";
export interface IAnchorAttributes extends IDistance {
allowOverlap?: "0" | "1";
behindDoc?: "0" | "1";
layoutInCell?: "0" | "1";
locked?: "0" | "1";
relativeHeight?: number;
simplePos?: "0" | "1";
readonly allowOverlap?: "0" | "1";
readonly behindDoc?: "0" | "1";
readonly layoutInCell?: "0" | "1";
readonly locked?: "0" | "1";
readonly relativeHeight?: number;
readonly simplePos?: "0" | "1";
}
export class AnchorAttributes extends XmlAttributeComponent<IAnchorAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
distT: "distT",
distB: "distB",
distL: "distL",

View File

@ -1,10 +1,12 @@
import { assert } from "chai";
import { Utility } from "../../../tests/utility";
import { IDrawingOptions, TextWrapStyle } from ".././";
import { Anchor } from "./";
import { Utility } from "tests/utility";
function createDrawing(drawingOptions: IDrawingOptions): Anchor {
import { IDrawingOptions } from "../drawing";
import { TextWrappingType } from "../text-wrap";
import { Anchor } from "./anchor";
function createAnchor(drawingOptions: IDrawingOptions): Anchor {
return new Anchor(
1,
{
@ -26,14 +28,32 @@ describe("Anchor", () => {
describe("#constructor()", () => {
it("should create a Drawing with correct root key", () => {
anchor = createDrawing({});
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
},
});
const newJson = Utility.jsonify(anchor);
assert.equal(newJson.rootKey, "wp:anchor");
assert.equal(newJson.root.length, 10);
});
it("should create a Drawing with all default options", () => {
anchor = createDrawing({});
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
},
});
const newJson = Utility.jsonify(anchor);
assert.equal(newJson.root.length, 10);
@ -58,7 +78,7 @@ describe("Anchor", () => {
const horizontalPosition = newJson.root[2];
assert.equal(horizontalPosition.rootKey, "wp:positionH");
assert.include(horizontalPosition.root[0].root, {
relativeFrom: "column",
relativeFrom: "page",
});
assert.equal(horizontalPosition.root[1].rootKey, "wp:posOffset");
assert.include(horizontalPosition.root[1].root[0], 0);
@ -67,7 +87,7 @@ describe("Anchor", () => {
const verticalPosition = newJson.root[3];
assert.equal(verticalPosition.rootKey, "wp:positionV");
assert.include(verticalPosition.root[0].root, {
relativeFrom: "paragraph",
relativeFrom: "page",
});
assert.equal(verticalPosition.root[1].rootKey, "wp:posOffset");
assert.include(verticalPosition.root[1].root[0], 0);
@ -101,10 +121,18 @@ describe("Anchor", () => {
assert.equal(graphic.rootKey, "a:graphic");
});
it("should create a Drawing with text wrapping", () => {
anchor = createDrawing({
textWrapping: {
textWrapStyle: TextWrapStyle.SQUARE,
it("should create a Drawing with square text wrapping", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
wrap: {
type: TextWrappingType.SQUARE,
},
},
});
const newJson = Utility.jsonify(anchor);
@ -114,5 +142,68 @@ describe("Anchor", () => {
const textWrap = newJson.root[6];
assert.equal(textWrap.rootKey, "wp:wrapSquare");
});
it("should create a Drawing with no text wrapping", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
wrap: {
type: TextWrappingType.NONE,
},
},
});
const newJson = Utility.jsonify(anchor);
assert.equal(newJson.root.length, 10);
const textWrap = newJson.root[6];
assert.equal(textWrap.rootKey, "wp:wrapNone");
});
it("should create a Drawing with tight text wrapping", () => {
anchor = createAnchor({
floating: {
horizontalPosition: {
offset: 0,
},
verticalPosition: {
offset: 0,
},
wrap: {
type: TextWrappingType.TIGHT,
},
},
});
const newJson = Utility.jsonify(anchor);
assert.equal(newJson.root.length, 10);
const textWrap = newJson.root[6];
assert.equal(textWrap.rootKey, "wp:wrapTight");
});
it("should create a Drawing with tight text wrapping", () => {
anchor = createAnchor({
floating: {
verticalPosition: {
offset: 0,
},
horizontalPosition: {
offset: 0,
},
wrap: {
type: TextWrappingType.TOP_AND_BOTTOM,
},
},
});
const newJson = Utility.jsonify(anchor);
assert.equal(newJson.root.length, 10);
const textWrap = newJson.root[6];
assert.equal(textWrap.rootKey, "wp:wrapTopAndBottom");
});
});
});

View File

@ -2,16 +2,9 @@
import { IMediaDataDimensions } from "file/media";
import { XmlComponent } from "file/xml-components";
import { IDrawingOptions } from "../drawing";
import {
HorizontalPosition,
HorizontalPositionRelativeFrom,
IFloating,
SimplePos,
VerticalPosition,
VerticalPositionRelativeFrom,
} from "../floating";
import { HorizontalPosition, IFloating, SimplePos, VerticalPosition } from "../floating";
import { Graphic } from "../inline/graphic";
import { TextWrapStyle, WrapNone, WrapSquare, WrapTight, WrapTopAndBottom } from "../text-wrap";
import { TextWrappingType, WrapNone, WrapSquare, WrapTight, WrapTopAndBottom } from "../text-wrap";
import { DocProperties } from "./../doc-properties/doc-properties";
import { EffectExtent } from "./../effect-extent/effect-extent";
import { Extent } from "./../extent/extent";
@ -23,14 +16,8 @@ const defaultOptions: IFloating = {
behindDocument: false,
lockAnchor: false,
layoutInCell: true,
verticalPosition: {
relative: VerticalPositionRelativeFrom.PARAGRAPH,
offset: 0,
},
horizontalPosition: {
relative: HorizontalPositionRelativeFrom.COLUMN,
offset: 0,
},
verticalPosition: {},
horizontalPosition: {},
};
export class Anchor extends XmlComponent {
@ -38,15 +25,22 @@ export class Anchor extends XmlComponent {
super("wp:anchor");
const floating = {
margins: {
top: 0,
bottom: 0,
left: 0,
right: 0,
},
...defaultOptions,
...drawingOptions.floating,
};
this.root.push(
new AnchorAttributes({
distT: 0,
distB: 0,
distL: 0,
distR: 0,
distT: floating.margins.top || 0,
distB: floating.margins.bottom || 0,
distL: floating.margins.left || 0,
distR: floating.margins.right || 0,
simplePos: "0", // note: word doesn't fully support - so we use 0
allowOverlap: floating.allowOverlap === true ? "1" : "0",
behindDoc: floating.behindDocument === true ? "1" : "0",
@ -62,18 +56,18 @@ export class Anchor extends XmlComponent {
this.root.push(new Extent(dimensions.emus.x, dimensions.emus.y));
this.root.push(new EffectExtent());
if (drawingOptions.textWrapping !== undefined) {
switch (drawingOptions.textWrapping.textWrapStyle) {
case TextWrapStyle.SQUARE:
this.root.push(new WrapSquare(drawingOptions.textWrapping));
if (drawingOptions.floating !== undefined && drawingOptions.floating.wrap !== undefined) {
switch (drawingOptions.floating.wrap.type) {
case TextWrappingType.SQUARE:
this.root.push(new WrapSquare(drawingOptions.floating.wrap, drawingOptions.floating.margins));
break;
case TextWrapStyle.TIGHT:
this.root.push(new WrapTight(drawingOptions.textWrapping.distanceFromText));
case TextWrappingType.TIGHT:
this.root.push(new WrapTight(drawingOptions.floating.margins));
break;
case TextWrapStyle.TOP_AND_BOTTOM:
this.root.push(new WrapTopAndBottom(drawingOptions.textWrapping.distanceFromText));
case TextWrappingType.TOP_AND_BOTTOM:
this.root.push(new WrapTopAndBottom(drawingOptions.floating.margins));
break;
case TextWrapStyle.NONE:
case TextWrappingType.NONE:
default:
this.root.push(new WrapNone());
}

View File

@ -1,13 +1,13 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IDocPropertiesAttributes {
id?: number;
name?: string;
descr?: string;
readonly id?: number;
readonly name?: string;
readonly descr?: string;
}
export class DocPropertiesAttributes extends XmlAttributeComponent<IDocPropertiesAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
id: "id",
name: "name",
descr: "descr",

View File

@ -1,7 +1,8 @@
import { assert } from "chai";
import { Utility } from "../../tests/utility";
import { Drawing, IDrawingOptions, PlacementPosition } from "./";
import { Utility } from "tests/utility";
import { Drawing, IDrawingOptions } from "./drawing";
const imageBase64Data = `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`;
@ -46,7 +47,14 @@ describe("Drawing", () => {
it("should create a drawing with anchor element when there options are passed", () => {
currentBreak = createDrawing({
position: PlacementPosition.FLOATING,
floating: {
horizontalPosition: {
offset: 0,
},
verticalPosition: {
offset: 0,
},
},
});
const newJson = Utility.jsonify(currentBreak);
assert.equal(newJson.root[0].rootKey, "wp:anchor");

View File

@ -3,50 +3,33 @@ import { XmlComponent } from "file/xml-components";
import { Anchor } from "./anchor";
import { IFloating } from "./floating";
import { Inline } from "./inline";
import { ITextWrapping } from "./text-wrap";
export enum PlacementPosition {
INLINE,
FLOATING,
}
export interface IDistance {
distT?: number;
distB?: number;
distL?: number;
distR?: number;
readonly distT?: number;
readonly distB?: number;
readonly distL?: number;
readonly distR?: number;
}
export interface IDrawingOptions {
position?: PlacementPosition;
textWrapping?: ITextWrapping;
floating?: IFloating;
readonly floating?: IFloating;
}
const defaultDrawingOptions: IDrawingOptions = {
position: PlacementPosition.INLINE,
};
export class Drawing extends XmlComponent {
private readonly inline: Inline;
constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) {
constructor(imageData: IMediaData, drawingOptions: IDrawingOptions = {}) {
super("w:drawing");
if (imageData === undefined) {
throw new Error("imageData cannot be undefined");
}
const mergedOptions = {
...defaultDrawingOptions,
...drawingOptions,
};
if (mergedOptions.position === PlacementPosition.INLINE) {
if (!drawingOptions.floating) {
this.inline = new Inline(imageData.referenceId, imageData.dimensions);
this.root.push(this.inline);
} else if (mergedOptions.position === PlacementPosition.FLOATING) {
this.root.push(new Anchor(imageData.referenceId, imageData.dimensions, mergedOptions));
} else {
this.root.push(new Anchor(imageData.referenceId, imageData.dimensions, drawingOptions));
}
}

View File

@ -1,14 +1,14 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IEffectExtentAttributes {
b?: number;
l?: number;
r?: number;
t?: number;
readonly b?: number;
readonly l?: number;
readonly r?: number;
readonly t?: number;
}
export class EffectExtentAttributes extends XmlAttributeComponent<IEffectExtentAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
b: "b",
l: "l",
r: "r",

View File

@ -1,12 +1,12 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IExtentAttributes {
cx?: number;
cy?: number;
readonly cx?: number;
readonly cy?: number;
}
export class ExtentAttributes extends XmlAttributeComponent<IExtentAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
cx: "cx",
cy: "cy",
};

View File

@ -1,4 +1,5 @@
import { XmlComponent } from "file/xml-components";
import { ExtentAttributes } from "./extent-attributes";
export class Extent extends XmlComponent {

View File

@ -1,8 +1,9 @@
import { assert } from "chai";
import { VerticalPositionAlign } from ".";
import { Utility } from "../../../tests/utility";
import { Utility } from "tests/utility";
import { Align } from "./align";
import { VerticalPositionAlign } from "./floating-position";
describe("Align", () => {
describe("#constructor()", () => {

View File

@ -1,4 +1,5 @@
// http://officeopenxml.com/drwPicFloating-position.php
import { ITextWrapping } from "../text-wrap";
export enum HorizontalPositionRelativeFrom {
CHARACTER = "character",
@ -39,22 +40,31 @@ export enum VerticalPositionAlign {
}
export interface IHorizontalPositionOptions {
relative: HorizontalPositionRelativeFrom;
align?: HorizontalPositionAlign;
offset?: number;
readonly relative?: HorizontalPositionRelativeFrom;
readonly align?: HorizontalPositionAlign;
readonly offset?: number;
}
export interface IVerticalPositionOptions {
relative: VerticalPositionRelativeFrom;
align?: VerticalPositionAlign;
offset?: number;
readonly relative?: VerticalPositionRelativeFrom;
readonly align?: VerticalPositionAlign;
readonly offset?: number;
}
export interface IMargins {
readonly left?: number;
readonly bottom?: number;
readonly top?: number;
readonly right?: number;
}
export interface IFloating {
horizontalPosition: IHorizontalPositionOptions;
verticalPosition: IVerticalPositionOptions;
allowOverlap?: boolean;
lockAnchor?: boolean;
behindDocument?: boolean;
layoutInCell?: boolean;
readonly horizontalPosition: IHorizontalPositionOptions;
readonly verticalPosition: IVerticalPositionOptions;
readonly allowOverlap?: boolean;
readonly lockAnchor?: boolean;
readonly behindDocument?: boolean;
readonly layoutInCell?: boolean;
readonly margins?: IMargins;
readonly wrap?: ITextWrapping;
}

View File

@ -1,7 +1,8 @@
import { assert } from "chai";
import { HorizontalPositionAlign, HorizontalPositionRelativeFrom } from ".";
import { Utility } from "../../../tests/utility";
import { Utility } from "tests/utility";
import { HorizontalPositionAlign, HorizontalPositionRelativeFrom } from "./floating-position";
import { HorizontalPosition } from "./horizontal-position";
describe("HorizontalPosition", () => {

View File

@ -5,11 +5,11 @@ import { HorizontalPositionRelativeFrom, IHorizontalPositionOptions } from "./fl
import { PositionOffset } from "./position-offset";
interface IHorizontalPositionAttributes {
relativeFrom: HorizontalPositionRelativeFrom;
readonly relativeFrom: HorizontalPositionRelativeFrom;
}
class HorizontalPositionAttributes extends XmlAttributeComponent<IHorizontalPositionAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
relativeFrom: "relativeFrom",
};
}
@ -20,7 +20,7 @@ export class HorizontalPosition extends XmlComponent {
this.root.push(
new HorizontalPositionAttributes({
relativeFrom: horizontalPosition.relative,
relativeFrom: horizontalPosition.relative || HorizontalPositionRelativeFrom.PAGE,
}),
);

View File

@ -1,6 +1,7 @@
import { assert } from "chai";
import { Utility } from "../../../tests/utility";
import { Utility } from "tests/utility";
import { PositionOffset } from "./position-offset";
describe("PositionOffset", () => {

View File

@ -1,6 +1,7 @@
import { assert } from "chai";
import { Utility } from "../../../tests/utility";
import { Utility } from "tests/utility";
import { SimplePos } from "./simple-pos";
describe("SimplePos", () => {

View File

@ -2,12 +2,12 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
interface ISimplePosAttributes {
x: number;
y: number;
readonly x: number;
readonly y: number;
}
class SimplePosAttributes extends XmlAttributeComponent<ISimplePosAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
x: "x",
y: "y",
};

View File

@ -1,7 +1,8 @@
import { assert } from "chai";
import { VerticalPositionAlign, VerticalPositionRelativeFrom } from ".";
import { Utility } from "../../../tests/utility";
import { Utility } from "tests/utility";
import { VerticalPositionAlign, VerticalPositionRelativeFrom } from "./floating-position";
import { VerticalPosition } from "./vertical-position";
describe("VerticalPosition", () => {

View File

@ -5,11 +5,11 @@ import { IVerticalPositionOptions, VerticalPositionRelativeFrom } from "./floati
import { PositionOffset } from "./position-offset";
interface IVerticalPositionAttributes {
relativeFrom: VerticalPositionRelativeFrom;
readonly relativeFrom: VerticalPositionRelativeFrom;
}
class VerticalPositionAttributes extends XmlAttributeComponent<IVerticalPositionAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
relativeFrom: "relativeFrom",
};
}
@ -20,7 +20,7 @@ export class VerticalPosition extends XmlComponent {
this.root.push(
new VerticalPositionAttributes({
relativeFrom: verticalPosition.relative,
relativeFrom: verticalPosition.relative || VerticalPositionRelativeFrom.PAGE,
}),
);

View File

@ -1,12 +1,12 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IGraphicFrameLockAttributes {
xmlns?: string;
noChangeAspect?: number;
readonly xmlns?: string;
readonly noChangeAspect?: number;
}
export class GraphicFrameLockAttributes extends XmlAttributeComponent<IGraphicFrameLockAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
xmlns: "xmlns:a",
noChangeAspect: "noChangeAspect",
};

View File

@ -1,11 +1,11 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IGraphicDataAttributes {
uri?: string;
readonly uri?: string;
}
export class GraphicDataAttributes extends XmlAttributeComponent<IGraphicDataAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
uri: "uri",
};
}

View File

@ -1,12 +1,12 @@
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
interface IBlipProperties {
embed: string;
cstate: string;
readonly embed: string;
readonly cstate: string;
}
class BlipAttributes extends XmlAttributeComponent<IBlipProperties> {
protected xmlKeys = {
protected readonly xmlKeys = {
embed: "r:embed",
cstate: "cstate",
};

View File

@ -1,12 +1,12 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IPicLocksAttributes {
noChangeAspect?: number;
noChangeArrowheads?: number;
readonly noChangeAspect?: number;
readonly noChangeArrowheads?: number;
}
export class PicLocksAttributes extends XmlAttributeComponent<IPicLocksAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
noChangeAspect: "noChangeAspect",
noChangeArrowheads: "noChangeArrowheads",
};

View File

@ -1,13 +1,13 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface INonVisualPropertiesAttributes {
id?: number;
name?: string;
descr?: string;
readonly id?: number;
readonly name?: string;
readonly descr?: string;
}
export class NonVisualPropertiesAttributes extends XmlAttributeComponent<INonVisualPropertiesAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
id: "id",
name: "name",
descr: "desc",

View File

@ -1,11 +1,11 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IPicAttributes {
xmlns?: string;
readonly xmlns?: string;
}
export class PicAttributes extends XmlAttributeComponent<IPicAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
xmlns: "xmlns:pic",
};
}

View File

@ -1,12 +1,12 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IExtentsAttributes {
cx?: number;
cy?: number;
readonly cx?: number;
readonly cy?: number;
}
export class ExtentsAttributes extends XmlAttributeComponent<IExtentsAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
cx: "cx",
cy: "cy",
};

View File

@ -1,12 +1,12 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IOffsetAttributes {
x?: number;
y?: number;
readonly x?: number;
readonly y?: number;
}
export class OffsetAttributes extends XmlAttributeComponent<IOffsetAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
x: "x",
y: "y",
};

View File

@ -1,11 +1,11 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IPresetGeometryAttributes {
prst?: string;
readonly prst?: string;
}
export class PresetGeometryAttributes extends XmlAttributeComponent<IPresetGeometryAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
prst: "prst",
};
}

View File

@ -1,11 +1,11 @@
import { XmlAttributeComponent } from "file/xml-components";
export interface IShapePropertiesAttributes {
bwMode?: string;
readonly bwMode?: string;
}
export class ShapePropertiesAttributes extends XmlAttributeComponent<IShapePropertiesAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
bwMode: "bwMode",
};
}

View File

@ -2,11 +2,11 @@ import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { GraphicData } from "./graphic-data";
interface IGraphicProperties {
a: string;
readonly a: string;
}
class GraphicAttributes extends XmlAttributeComponent<IGraphicProperties> {
protected xmlKeys = {
protected readonly xmlKeys = {
a: "xmlns:a",
};
}

View File

@ -1,11 +1,16 @@
import { XmlAttributeComponent } from "file/xml-components";
import { IDistance } from "../drawing";
// tslint:disable-next-line:no-empty-interface
export interface IInlineAttributes extends IDistance {}
// distT, distB etc have no effect on inline images, only floating
export interface IInlineAttributes extends IDistance {
readonly distT?: number;
readonly distB?: number;
readonly distL?: number;
readonly distR?: number;
}
export class InlineAttributes extends XmlAttributeComponent<IInlineAttributes> {
protected xmlKeys = {
protected readonly xmlKeys = {
distT: "distT",
distB: "distB",
distL: "distL",

View File

@ -1,14 +1,14 @@
// http://officeopenxml.com/drwPicFloating-textWrap.php
import { IDistance } from "../drawing";
export enum TextWrapStyle {
export enum TextWrappingType {
NONE,
SQUARE,
TIGHT,
TOP_AND_BOTTOM,
}
export enum WrapTextOption {
export enum TextWrappingSide {
BOTH_SIDES = "bothSides",
LEFT = "left",
RIGHT = "right",
@ -16,7 +16,7 @@ export enum WrapTextOption {
}
export interface ITextWrapping {
textWrapStyle: TextWrapStyle;
wrapTextOption?: WrapTextOption;
distanceFromText?: IDistance;
readonly type: TextWrappingType;
readonly side?: TextWrappingSide;
readonly margins?: IDistance;
}

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