Compare commits

..

46 Commits
8.2.1 ... 8.2.4

Author SHA1 Message Date
3eb98533ae Version bump 2023-11-02 03:02:21 +00:00
f5f021834e Merge pull request #2403 from dolanmiu/feature/bump-eslint-plugin
Upgrade @typescript-eslint/parser
2023-11-02 03:01:55 +00:00
35702c3f77 Upgrade @typescript-eslint/parser 2023-11-01 22:52:41 +00:00
8fbbd571ad Merge pull request #2402 from dolanmiu/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-6.9.1
build(deps-dev): bump @typescript-eslint/eslint-plugin from 5.60.1 to 6.9.1
2023-11-01 22:27:36 +00:00
2dd228be69 build(deps-dev): bump @typescript-eslint/eslint-plugin
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.60.1 to 6.9.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.9.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-01 21:16:40 +00:00
562835cfe7 Merge pull request #2401 from dolanmiu/dependabot/npm_and_yarn/vite-4.5.0
build(deps-dev): bump vite from 4.3.9 to 4.5.0
2023-11-01 21:15:57 +00:00
6bac06e84d Merge pull request #2382 from dolanmiu/dependabot/npm_and_yarn/cspell-7.3.8
build(deps-dev): bump cspell from 6.31.1 to 7.3.8
2023-11-01 21:15:33 +00:00
88436168ee Merge pull request #2400 from dolanmiu/dependabot/npm_and_yarn/browserify-sign-4.2.2
build(deps-dev): bump browserify-sign from 4.2.1 to 4.2.2
2023-11-01 21:15:26 +00:00
0c3206d2e2 Merge pull request #2350 from dolanmiu/dependabot/npm_and_yarn/eslint-plugin-jsdoc-46.8.2
build(deps-dev): bump eslint-plugin-jsdoc from 46.4.4 to 46.8.2
2023-11-01 21:15:08 +00:00
033debd339 Merge pull request #2303 from dolanmiu/dependabot/npm_and_yarn/execa-8.0.1
build(deps-dev): bump execa from 7.1.1 to 8.0.1
2023-11-01 21:15:00 +00:00
d4f160732a Use Record instead of Object 2023-11-01 20:56:02 +00:00
4238fc9ab4 build(deps-dev): bump execa from 7.1.1 to 8.0.1
Bumps [execa](https://github.com/sindresorhus/execa) from 7.1.1 to 8.0.1.
- [Release notes](https://github.com/sindresorhus/execa/releases)
- [Commits](https://github.com/sindresorhus/execa/compare/v7.1.1...v8.0.1)

---
updated-dependencies:
- dependency-name: execa
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-01 20:55:59 +00:00
0d042b8dd1 build(deps-dev): bump vite from 4.3.9 to 4.5.0
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.3.9 to 4.5.0.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.5.0/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-01 20:55:35 +00:00
afdd5f2d8f Merge pull request #2383 from dolanmiu/dependabot/npm_and_yarn/types/node-20.8.6
build(deps): bump @types/node from 20.4.2 to 20.8.6
2023-11-01 20:55:29 +00:00
70c7b3d1b3 Merge pull request #2374 from dolanmiu/dependabot/npm_and_yarn/postcss-8.4.31
build(deps-dev): bump postcss from 8.4.23 to 8.4.31
2023-11-01 20:55:22 +00:00
7b1cd5fe86 build(deps-dev): bump browserify-sign from 4.2.1 to 4.2.2
Bumps [browserify-sign](https://github.com/crypto-browserify/browserify-sign) from 4.2.1 to 4.2.2.
- [Changelog](https://github.com/browserify/browserify-sign/blob/main/CHANGELOG.md)
- [Commits](https://github.com/crypto-browserify/browserify-sign/compare/v4.2.1...v4.2.2)

---
updated-dependencies:
- dependency-name: browserify-sign
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-01 20:55:02 +00:00
09ab169ffd Merge pull request #2359 from dolanmiu/dependabot/npm_and_yarn/glob-10.3.10
build(deps-dev): bump glob from 10.3.1 to 10.3.10
2023-11-01 20:55:01 +00:00
35a82cf12e Merge pull request #2333 from dolanmiu/dependabot/npm_and_yarn/inquirer-9.2.11
build(deps-dev): bump inquirer from 9.2.7 to 9.2.11
2023-11-01 20:54:54 +00:00
8d33cb01dd Merge pull request #2268 from dolanmiu/dependabot/npm_and_yarn/eslint-plugin-unicorn-48.0.1
build(deps-dev): bump eslint-plugin-unicorn from 47.0.0 to 48.0.1
2023-11-01 20:54:35 +00:00
438d11dd86 Merge pull request #2387 from wilkmaia/fix/patch-styling
Fix style being missed on patchDocument
2023-11-01 20:54:23 +00:00
d23b0d0789 Fix coverage 2023-11-01 20:40:30 +00:00
1fa8c7ac82 Merge pull request #2397 from hom/master
fix: #2082 Numbering instance restart number not work since v8.0.0 and work well in v7.8.2
2023-10-27 00:41:01 +01:00
3d6ead0359 Merge pull request #2 from hom/fix/#2082 2023-10-27 03:59:58 +08:00
379050dccd Merge pull request #1 from dolanmiu/master 2023-10-27 03:31:07 +08:00
f9d1c197cf Merge pull request #2379 from rahon6000/master
changed default section margin from inch to Twip
2023-10-20 22:10:03 +01:00
a89ee463e6 Fix style being missed on patchDocument
There were scenarios in which patching a document would result in loss
of style for the template runs and, possibly, their right-adjacent run
as well (post-splitting). That was due to the run style elements not
being added to the newly created runs.

This commit addresses this issue by introducing a new, optional, flag
for the `patchDocument` function: `keepOriginalStyles`. It defaults to
`false` (current behavior) and, when `true`, ensures that there is no
loss of styling.

This should address https://github.com/dolanmiu/docx/issues/2293.
2023-10-18 12:58:01 -03:00
036caaacc8 build(deps): bump @types/node from 20.4.2 to 20.8.6
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.4.2 to 20.8.6.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-16 11:53:19 +00:00
80c37afe2b build(deps-dev): bump cspell from 6.31.1 to 7.3.8
Bumps [cspell](https://github.com/streetsidesoftware/cspell) from 6.31.1 to 7.3.8.
- [Release notes](https://github.com/streetsidesoftware/cspell/releases)
- [Changelog](https://github.com/streetsidesoftware/cspell/blob/main/CHANGELOG.md)
- [Commits](https://github.com/streetsidesoftware/cspell/compare/v6.31.1...v7.3.8)

---
updated-dependencies:
- dependency-name: cspell
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-16 11:52:24 +00:00
3a36d912fe changed default section margin from inch to Twip 2023-10-12 12:14:13 +09:00
c741d5d8ac build(deps-dev): bump postcss from 8.4.23 to 8.4.31
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.23 to 8.4.31.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.23...8.4.31)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-07 19:43:49 +00:00
0fa7dd13ad build(deps-dev): bump glob from 10.3.1 to 10.3.10
Bumps [glob](https://github.com/isaacs/node-glob) from 10.3.1 to 10.3.10.
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v10.3.1...v10.3.10)

---
updated-dependencies:
- dependency-name: glob
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-27 11:43:28 +00:00
d5d80550e7 Version bump 2023-09-26 23:00:51 +01:00
23a0a59454 Use .mjs for ESM 2023-09-26 22:59:32 +01:00
066aa56f6a build(deps-dev): bump eslint-plugin-jsdoc from 46.4.4 to 46.8.2
Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 46.4.4 to 46.8.2.
- [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases)
- [Changelog](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/.releaserc)
- [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v46.4.4...v46.8.2)

---
updated-dependencies:
- dependency-name: eslint-plugin-jsdoc
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-21 11:59:15 +00:00
3997b7a5d6 fix: #2082 Numbering instance restart number not work since v8.0.0 and work well in v7.8.2 2023-09-13 17:08:59 +08:00
ccc391607a Merge pull request #2331 from hshoja/doc/update-style-with-xml
Update styling with xml  docs
2023-09-11 21:04:32 +01:00
9577192d41 build(deps-dev): bump inquirer from 9.2.7 to 9.2.11
Bumps [inquirer](https://github.com/SBoudrias/Inquirer.js) from 9.2.7 to 9.2.11.
- [Release notes](https://github.com/SBoudrias/Inquirer.js/releases)
- [Commits](https://github.com/SBoudrias/Inquirer.js/compare/inquirer@9.2.7...inquirer@9.2.11)

---
updated-dependencies:
- dependency-name: inquirer
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-11 11:40:53 +00:00
968c3aed0f Update styling-with-xml doc 2023-09-10 15:57:48 +02:00
9c7a80729b Merge branch 'master' of github.com:dolanmiu/docx 2023-08-06 20:10:11 +01:00
5f26ca1c94 Update numbering docs 2023-08-06 20:10:03 +01:00
f2b1587bff build(deps-dev): bump eslint-plugin-unicorn from 47.0.0 to 48.0.1
Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 47.0.0 to 48.0.1.
- [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases)
- [Commits](https://github.com/sindresorhus/eslint-plugin-unicorn/compare/v47.0.0...v48.0.1)

---
updated-dependencies:
- dependency-name: eslint-plugin-unicorn
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-26 11:15:05 +00:00
3b9f80fb1a Merge pull request #2259 from dolanmiu/feat/bump-prettier
Bump prettier
2023-07-21 22:13:17 +01:00
8da57bec51 Bump prettier
Fix new prettier issues
2023-07-21 20:11:52 +01:00
63db9f290c Version bump 2023-07-20 22:44:30 +01:00
e8bd4bd6c6 Merge pull request #2256 from dolanmiu/feature/tweak-build
Remove default browser export
2023-07-20 22:42:56 +01:00
28d93b0c42 Remove default browser export
Fixes Angular issue
2023-07-20 20:46:53 +01:00
21 changed files with 1995 additions and 1459 deletions

View File

@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head> <head>
<script src="../build/index.umd.js"></script> <script src="../build/index.umd.js"></script>

View File

@ -1,5 +1,7 @@
# Bullets and Numbering # Bullets and Numbering
!> Bullets and Numbering requires an understanding of [Sections](usage/sections.md) and [Paragraphs](usage/paragraph.md).
`docx` is quite flexible in its bullets and numbering system, allowing `docx` is quite flexible in its bullets and numbering system, allowing
the user great freedom in how bullets and numbers are to be styled and the user great freedom in how bullets and numbers are to be styled and
displayed. E.g., numbers can be shown using Arabic numerals, roman displayed. E.g., numbers can be shown using Arabic numerals, roman
@ -8,112 +10,128 @@ format also supports re-using bullets/numbering styles throughout the
document, so that different lists using the same style need not document, so that different lists using the same style need not
redefine them. redefine them.
Because of this flexibility, bullets and numbering in DOCX involves a ## Configuration
couple of moving pieces:
1. Document-level bullets/numbering definitions (abstract) Numbering is configured by adding config into `Document`:
2. Document-level bullets/numbering definitions (concrete)
3. Paragraph-level bullets/numbering selection
## Document-level bullets/numbering definitions (abstract)
Every document contains a set of abstract bullets/numbering
definitions which define the formatting and layout of paragraphs using
those bullets/numbering. An abstract numbering system defines how
bullets/numbers are to be shown for lists, including any sublists that
may be used. Thus each abstract definition includes a series of
_levels_ which form a sequence starting at 0 indicating the top-level
list look and increasing from there to describe the sublists, then
sub-sublists, etc. Each level includes the following properties:
* **level**: This is its 0-based index in the definition stack
* **numberFormat**: This indicates how the bullet or number should be
generated. Options include `bullet` (meaning don't count), `decimal`
(arabic numerals), `upperRoman`, `lowerRoman`, `hex`, and many
more.
* **levelText**: This is a format string using the output of the
`numberFormat` function and generating a string to insert before
every item in the list. You may use `%1`, `%2`, ... to reference the
numbers from each numbering level before this one. Thus a level
text of `%d)` with a number format of `lowerLetter` would result in
the sequence "a)", "b)", ...
* and a few others, which you can see in the OOXML spec section 17.9.6
## Document-level bullets/numbering definitions (concrete)
Concrete definitions are sort of like concrete subclasses of the
abstract definitions. They indicate their parent and are allowed to
override certain level definitions. Thus two lists that differ only in
how sub-sub-lists are to be displayed can share the same abstract
numbering definition and have slightly different concrete definitions.
## Paragraph-level bullets/numbering selection
In order to use a bullets/numbering definition (which must be
concrete), paragraphs need to select it, similar to applying a CSS
class to an element, using both the concrete numbering definition ID
and the level number that the paragraph should be at. Additionally, MS
Word and LibreOffice typically apply a "ListParagraph" style to
paragraphs that are being numbered.
## Using bullets/numbering in `docx`
`docx` includes a pre-defined bullet style which you can add to your
paragraphs using `para.bullets()`. If you require different bullet
styles or numbering of any kind, you'll have to use the
`docx.Numbering` class.
First you need to create a new numbering container class and use it to
create your abstract numbering style, define your levels, and create
your concrete numbering style:
```ts ```ts
const numbering = new docx.Numbering(); new Document({
numbering: {
const abstractNum = numbering.createAbstractNumbering(); config: [...]
abstractNum.createLevel(0, "upperRoman", "%1", "start").addParagraphProperty(new Indent(720, 260)); }
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(abstractNum);
``` ```
You can then apply your concrete style to paragraphs using the Each `config` entry includes the following properties:
`setNumbering` method:
. Each level includes the following properties:
| Property | Type | Notes | Possible Values |
| --------- | ----------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| reference | `string` | Required | A unique `string` |
| levels | `ILevelOptions[]` | Required | a series of _levels_ which form a sequence starting at 0 indicating the top-level list look and increasing from there to describe the sublists, then sub-sublists, etc |
### Level Options
Levels define the numbering definition itself, what it looks like, the indention, the alignment and the style. The reason why it is an array is because it allows the ability to create sub-lists. A sub list will have a different configuration because you may want the sub-list to have a different indentation or different bullet.
| Property | Type | Notes | Possible Values |
| --------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| level | `number` | Required | The list level this definition is for. `0` is for the root level, `1` is for a sub list, `2` is for a sub-sub-list etc. |
| format | `LevelFormat` | Optional | `DECIMAL`, `UPPER_ROMAN`, `LOWER_ROMAN`, `UPPER_LETTER`, `LOWER_LETTER`, `ORDINAL`, `CARDINAL_TEXT`, `ORDINAL_TEXT`, `HEX`, `CHICAGO`, `IDEOGRAPH__DIGITAL`, `JAPANESE_COUNTING`, `AIUEO`, `IROHA`, `DECIMAL_FULL_WIDTH`, `DECIMAL_HALF_WIDTH`, `JAPANESE_LEGAL`, `JAPANESE_DIGITAL_TEN_THOUSAND`, `DECIMAL_ENCLOSED_CIRCLE`, `DECIMAL_FULL_WIDTH2`, `AIUEO_FULL_WIDTH`, `IROHA_FULL_WIDTH`, `DECIMAL_ZERO`, `BULLET`, `GANADA`, `CHOSUNG`, `DECIMAL_ENCLOSED_FULLSTOP`, `DECIMAL_ENCLOSED_PARENTHESES`, `DECIMAL_ENCLOSED_CIRCLE_CHINESE`, `IDEOGRAPH_ENCLOSED_CIRCLE`, `IDEOGRAPH_TRADITIONAL`, `IDEOGRAPH_ZODIAC`, `IDEOGRAPH_ZODIAC_TRADITIONAL`, `TAIWANESE_COUNTING`, `IDEOGRAPH_LEGAL_TRADITIONAL`, `TAIWANESE_COUNTING_THOUSAND`, `TAIWANESE_DIGITAL`, `CHINESE_COUNTING`, `CHINESE_LEGAL_SIMPLIFIED`, `CHINESE_COUNTING_THOUSAND`, `KOREAN_DIGITAL`, `KOREAN_COUNTING`, `KOREAN_LEGAL`, `KOREAN_DIGITAL2`, `VIETNAMESE_COUNTING`, `RUSSIAN_LOWER`, `RUSSIAN_UPPER`, `NONE`, `NUMBER_IN_DASH`, `HEBREW1`, `HEBREW2`, `ARABIC_ALPHA`, `ARABIC_ABJAD`, `HINDI_VOWELS`, `HINDI_CONSONANTS`, `HINDI_NUMBERS`, `HINDI_COUNTING`, `THAI_LETTERS`, `THAI_NUMBERS`, `THAI_COUNTING`, `BAHT_TEXT`, `DOLLAR_TEXT`, `CUSTOM` |
| text | `string` | Optional | A unique `string` to describe the shape of the bullet |
| alignment | `string` | Required | `START`, `CENTER`, `END`, `BOTH`, `MEDIUM_KASHIDA`, `DISTRIBUTE`, `NUM_TAB`, `HIGH_KASHIDA`, `LOW_KASHIDA`, `THAI_DISTRIBUTE`, `LEFT`, `RIGHT`, `JUSTIFIED` |
| style | `string` | Optional | [Sections](usage/styling-with-js.md) |
## Using ordered lists in `docx`
Add a `numbering` section to the `Document` to numbering style, define your levels. Use `LevelFormat.UPPER_ROMAN` for the `format` in `levels`:
```ts ```ts
topLevelP.setNumbering(concrete, 0); const doc = new Document({
subP.setNumbering(concrete, 1); ...
subSubP.setNumbering(concrete, 2); numbering: {
``` config: [
{
## Unindent numbering reference: "my-numbering",
levels: [
Default:1. test {
After:1.test
Use default numbering have indent,If you want unindent numbering
How to custom number see the demo:
https://runkit.com/dolanmiu/docx-demo3
```ts
enum LevelSuffix {
NOTHING = "nothing",
SPACE = "space",
TAB = "tab"
}
// custom numbering
const levels=[
{
level: 0, level: 0,
format: "decimal", format: LevelFormat.UPPER_ROMAN,
text: "%1.", text: "%1",
alignment: AlignmentType.START, alignment: AlignmentType.START,
suffix: LevelSuffix.NOTHING, // Cancel intent style: {
}] paragraph: {
indent: { left: 2880, hanging: 2420 },
},
},
},
...
],
},
],
},
...
});
``` ```
And then on a `Paragraph`, we can add use the numbering created:
```ts
new Paragraph({
text: "Hey you!",
numbering: {
reference: "my-numbering",
level: 0,
},
}),
```
## Un-ordered lists / Bullet points
Add a `numbering` section to the `Document` to numbering style, define your levels. Use `LevelFormat.BULLET` for the `format` in `levels`:
```ts
const doc = new Document({
...
numbering: {
config: [
{
reference: "my-bullet-points",
levels: [
{
level: 0,
format: LevelFormat.BULLET,
text: "\u1F60",
alignment: AlignmentType.LEFT,
style: {
paragraph: {
indent: { left: convertInchesToTwip(0.5), hanging: convertInchesToTwip(0.25) },
},
},
},
],
},
],
},
...
});
```
And then on a `Paragraph`, we can add use the numbering created:
```ts
new Paragraph({
text: "Hey you!",
numbering: {
reference: "my-bullet-points",
level: 0,
},
}),
```
## Full Example
[Example](https://raw.githubusercontent.com/dolanmiu/docx/master/demo/3-numbering-and-bullet-points.ts ":include")
_Source: https://github.com/dolanmiu/docx/blob/master/demo/3-numbering-and-bullet-points.ts_

View File

@ -9,11 +9,16 @@
![image](https://user-images.githubusercontent.com/2917613/41195113-65edebfa-6c1f-11e8-97b4-77de2d60044a.png) ![image](https://user-images.githubusercontent.com/2917613/41195113-65edebfa-6c1f-11e8-97b4-77de2d60044a.png)
![image](https://user-images.githubusercontent.com/2917613/41195126-ca99c36c-6c1f-11e8-9e58-19e5f69b3b87.png) ![image](https://user-images.githubusercontent.com/2917613/41195126-ca99c36c-6c1f-11e8-9e58-19e5f69b3b87.png)
*Note*: Font and color selection from the theme are currently not supported.
3. You can even create a totally new `Style`: 3. You can even create a totally new `Style`:
![image](https://user-images.githubusercontent.com/2917613/41195135-f0f7862a-6c1f-11e8-8be4-dd6d8fe5be03.png) ![image](https://user-images.githubusercontent.com/2917613/41195135-f0f7862a-6c1f-11e8-8be4-dd6d8fe5be03.png)
![image](https://user-images.githubusercontent.com/2917613/41195139-0ec52130-6c20-11e8-8fae-f6b44b43fdf8.png) ![image](https://user-images.githubusercontent.com/2917613/41195139-0ec52130-6c20-11e8-8fae-f6b44b43fdf8.png)
*Note*: When selecting the style type, it is important to consider the component being used.
4. Save 4. Save
5. Re-name the saved `.docx` file to `.zip` and un-zip 5. Re-name the saved `.docx` file to `.zip` and un-zip
6. Find `styles.xml` 6. Find `styles.xml`

3031
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,17 @@
{ {
"name": "docx", "name": "docx",
"version": "8.2.1", "version": "8.2.4",
"description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.", "description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.",
"type": "module", "type": "module",
"main": "build/index.umd.js", "main": "build/index.umd.js",
"module": "./build/index.js", "module": "./build/index.mjs",
"types": "./build/index.d.ts", "types": "./build/index.d.ts",
"exports": { "exports": {
".": { ".": {
"browser": {
"default": "./build/index.umd.js"
},
"require": "./build/index.cjs", "require": "./build/index.cjs",
"types": "./build/index.d.ts", "types": "./build/index.d.ts",
"import": "./build/index.js", "import": "./build/index.mjs",
"default": "./build/index.js" "default": "./build/index.mjs"
} }
}, },
"files": [ "files": [
@ -70,16 +67,15 @@
}, },
"homepage": "https://docx.js.org", "homepage": "https://docx.js.org",
"devDependencies": { "devDependencies": {
"@esbuild/win32-x64": "^0.18.3",
"@types/inquirer": "^9.0.3", "@types/inquirer": "^9.0.3",
"@types/prompt": "^1.1.1", "@types/prompt": "^1.1.1",
"@types/unzipper": "^0.10.4", "@types/unzipper": "^0.10.4",
"@types/xml": "^1.0.8", "@types/xml": "^1.0.8",
"@typescript-eslint/eslint-plugin": "^5.36.1", "@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^5.36.1", "@typescript-eslint/parser": "^6.9.1",
"@vitest/coverage-v8": "^0.33.0", "@vitest/coverage-v8": "^0.33.0",
"@vitest/ui": "^0.33.0", "@vitest/ui": "^0.33.0",
"cspell": "^6.2.2", "cspell": "^7.3.8",
"docsify-cli": "^4.3.0", "docsify-cli": "^4.3.0",
"eslint": "^8.23.0", "eslint": "^8.23.0",
"eslint-plugin-functional": "^5.0.8", "eslint-plugin-functional": "^5.0.8",
@ -87,13 +83,13 @@
"eslint-plugin-jsdoc": "^46.2.6", "eslint-plugin-jsdoc": "^46.2.6",
"eslint-plugin-no-null": "^1.0.2", "eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-prefer-arrow": "^1.2.3", "eslint-plugin-prefer-arrow": "^1.2.3",
"eslint-plugin-unicorn": "^47.0.0", "eslint-plugin-unicorn": "^48.0.1",
"execa": "^7.1.1", "execa": "^8.0.1",
"glob": "^10.2.7", "glob": "^10.2.7",
"inquirer": "^9.2.7", "inquirer": "^9.2.7",
"jsdom": "^22.1.0", "jsdom": "^22.1.0",
"pre-commit": "^1.2.2", "pre-commit": "^1.2.2",
"prettier": "^2.3.1", "prettier": "^3.0.0",
"ts-node": "^10.2.1", "ts-node": "^10.2.1",
"tsconfig-paths": "^4.0.0", "tsconfig-paths": "^4.0.0",
"typedoc": "^0.24.8", "typedoc": "^0.24.8",

View File

@ -5,7 +5,6 @@ import { FooterWrapper } from "@file/footer-wrapper";
import { HeaderWrapper } from "@file/header-wrapper"; import { HeaderWrapper } from "@file/header-wrapper";
import { VerticalAlign, VerticalAlignElement } from "@file/vertical-align"; import { VerticalAlign, VerticalAlignElement } from "@file/vertical-align";
import { OnOffElement, XmlComponent } from "@file/xml-components"; import { OnOffElement, XmlComponent } from "@file/xml-components";
import { PositiveUniversalMeasure, UniversalMeasure } from "@util/values";
import { HeaderFooterReference, HeaderFooterReferenceType, HeaderFooterType } from "./properties/header-footer-reference"; import { HeaderFooterReference, HeaderFooterReferenceType, HeaderFooterType } from "./properties/header-footer-reference";
import { Columns, IColumnsAttributes } from "./properties/columns"; import { Columns, IColumnsAttributes } from "./properties/columns";
@ -76,10 +75,10 @@ export interface ISectionPropertiesOptions {
// </xsd:group> // </xsd:group>
export const sectionMarginDefaults = { export const sectionMarginDefaults = {
TOP: "1in" as UniversalMeasure, TOP: 1440,
RIGHT: "1in" as PositiveUniversalMeasure, RIGHT: 1440,
BOTTOM: "1in" as UniversalMeasure, BOTTOM: 1440,
LEFT: "1in" as PositiveUniversalMeasure, LEFT: 1440,
HEADER: 708, HEADER: 708,
FOOTER: 708, FOOTER: 708,
GUTTER: 0, GUTTER: 0,

View File

@ -80,6 +80,7 @@ export class Document extends XmlComponent {
} }
public add(item: Paragraph | Table | TableOfContents | ConcreteHyperlink): Document { public add(item: Paragraph | Table | TableOfContents | ConcreteHyperlink): Document {
// eslint-disable-next-line functional/immutable-data
this.body.push(item); this.body.push(item);
return this; return this;
} }

View File

@ -17,7 +17,11 @@ export class FooterWrapper implements IViewWrapper {
private readonly footer: Footer; private readonly footer: Footer;
private readonly relationships: Relationships; private readonly relationships: Relationships;
public constructor(private readonly media: Media, referenceId: number, initContent?: XmlComponent) { public constructor(
private readonly media: Media,
referenceId: number,
initContent?: XmlComponent,
) {
this.footer = new Footer(referenceId, initContent); this.footer = new Footer(referenceId, initContent);
this.relationships = new Relationships(); this.relationships = new Relationships();
} }

View File

@ -17,7 +17,11 @@ export class HeaderWrapper implements IViewWrapper {
private readonly header: Header; private readonly header: Header;
private readonly relationships: Relationships; private readonly relationships: Relationships;
public constructor(private readonly media: Media, referenceId: number, initContent?: XmlComponent) { public constructor(
private readonly media: Media,
referenceId: number,
initContent?: XmlComponent,
) {
this.header = new Header(referenceId, initContent); this.header = new Header(referenceId, initContent);
this.relationships = new Relationships(); this.relationships = new Relationships();
} }

View File

@ -216,7 +216,7 @@ export class Numbering extends XmlComponent {
abstractNumId: abstractNumbering.id, abstractNumId: abstractNumbering.id,
reference, reference,
instance, instance,
overrideLevel: overrideLevels: [
firstLevelStartNumber && Number.isInteger(firstLevelStartNumber) firstLevelStartNumber && Number.isInteger(firstLevelStartNumber)
? { ? {
num: 0, num: 0,
@ -226,6 +226,7 @@ export class Numbering extends XmlComponent {
num: 0, num: 0,
start: 1, start: 1,
}, },
],
}; };
this.concreteNumberingMap.set(fullReference, new ConcreteNumbering(concreteNumberingSettings)); this.concreteNumberingMap.set(fullReference, new ConcreteNumbering(concreteNumberingSettings));

View File

@ -1,5 +1,6 @@
// http://officeopenxml.com/WPparagraphProperties.php // http://officeopenxml.com/WPparagraphProperties.php
// https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_suppressLineNumbers_topic_ID0ECJAO.html // https://c-rex.net/projects/samples/ooxml/e1/Part4/OOXML_P4_DOCX_suppressLineNumbers_topic_ID0ECJAO.html
/* eslint-disable functional/immutable-data */
import { IContext, IgnoreIfEmptyXmlComponent, IXmlableObject, OnOffElement, XmlComponent } from "@file/xml-components"; import { IContext, IgnoreIfEmptyXmlComponent, IXmlableObject, OnOffElement, XmlComponent } from "@file/xml-components";
import { DocumentWrapper } from "../document-wrapper"; import { DocumentWrapper } from "../document-wrapper";
import { IShadingAttributesProperties, Shading } from "../shading"; import { IShadingAttributesProperties, Shading } from "../shading";

View File

@ -1,4 +1,5 @@
// https://www.ecma-international.org/wp-content/uploads/ECMA-376-1_5th_edition_december_2016.zip page 297, section 17.3.2.21 // https://www.ecma-international.org/wp-content/uploads/ECMA-376-1_5th_edition_december_2016.zip page 297, section 17.3.2.21
/* eslint-disable functional/immutable-data */
import { BorderElement, IBorderOptions } from "@file/border"; import { BorderElement, IBorderOptions } from "@file/border";
import { IShadingAttributesProperties, Shading } from "@file/shading"; import { IShadingAttributesProperties, Shading } from "@file/shading";
import { ChangeAttributes, IChangedAttributesProperties } from "@file/track-revision/track-revision"; import { ChangeAttributes, IChangedAttributesProperties } from "@file/track-revision/track-revision";

View File

@ -55,6 +55,7 @@ describe("ImportedXmlComponent", () => {
otherAttr: "2", otherAttr: "2",
}; };
importedXmlComponent = new ImportedXmlComponent("w:test", attributes); importedXmlComponent = new ImportedXmlComponent("w:test", attributes);
// eslint-disable-next-line functional/immutable-data
importedXmlComponent.push(new ImportedXmlComponent("w:child")); importedXmlComponent.push(new ImportedXmlComponent("w:child"));
}); });

View File

@ -21,6 +21,7 @@ export const convertToXmlComponent = (element: XmlElement): ImportedXmlComponent
for (const childElm of childElements) { for (const childElm of childElements) {
const child = convertToXmlComponent(childElm); const child = convertToXmlComponent(childElm);
if (child !== undefined) { if (child !== undefined) {
// eslint-disable-next-line functional/immutable-data
xmlComponent.push(child); xmlComponent.push(child);
} }
} }
@ -60,6 +61,7 @@ export class ImportedXmlComponent extends XmlComponent {
public constructor(rootKey: string, _attr?: any) { public constructor(rootKey: string, _attr?: any) {
super(rootKey); super(rootKey);
if (_attr) { if (_attr) {
// eslint-disable-next-line functional/immutable-data
this.root.push(new ImportedXmlComponentAttributes(_attr)); this.root.push(new ImportedXmlComponentAttributes(_attr));
} }
} }

View File

@ -20,11 +20,13 @@ describe("XmlComponent", () => {
}); });
it("should handle children elements", () => { it("should handle children elements", () => {
const xmlComponent = new TestComponent("w:test"); const xmlComponent = new TestComponent("w:test");
// eslint-disable-next-line functional/immutable-data
xmlComponent.push( xmlComponent.push(
new Attributes({ new Attributes({
val: "test", val: "test",
}), }),
); );
// eslint-disable-next-line functional/immutable-data
xmlComponent.push(new TestComponent("innerTest")); xmlComponent.push(new TestComponent("innerTest"));
const tree = new Formatter().format(xmlComponent); const tree = new Formatter().format(xmlComponent);
@ -43,6 +45,7 @@ describe("XmlComponent", () => {
}); });
it("should hoist attrs if only attrs are present", () => { it("should hoist attrs if only attrs are present", () => {
const xmlComponent = new TestComponent("w:test"); const xmlComponent = new TestComponent("w:test");
// eslint-disable-next-line functional/immutable-data
xmlComponent.push( xmlComponent.push(
new Attributes({ new Attributes({
val: "test", val: "test",

View File

@ -1,7 +1,7 @@
export interface IXmlAttribute { export interface IXmlAttribute {
readonly [key: string]: string | number | boolean; readonly [key: string]: string | number | boolean;
} }
export interface IXmlableObject extends Object { export interface IXmlableObject extends Record<string, unknown> {
// readonly _attr?: IXmlAttribute; // readonly _attr?: IXmlAttribute;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
readonly [key: string]: any; readonly [key: string]: any;

View File

@ -49,6 +49,7 @@ export type IPatch = ParagraphPatch | FilePatch;
export interface PatchDocumentOptions { export interface PatchDocumentOptions {
readonly patches: { readonly [key: string]: IPatch }; readonly patches: { readonly [key: string]: IPatch };
readonly keepOriginalStyles?: boolean;
} }
const imageReplacer = new ImageReplacer(); const imageReplacer = new ImageReplacer();
@ -68,11 +69,11 @@ export const patchDocument = async (data: InputDataType, options: PatchDocumentO
const hyperlinkRelationshipAdditions: IHyperlinkRelationshipAddition[] = []; const hyperlinkRelationshipAdditions: IHyperlinkRelationshipAddition[] = [];
let hasMedia = false; let hasMedia = false;
const binaryContentMap = new Map<string, Buffer>(); const binaryContentMap = new Map<string, Uint8Array>();
for (const [key, value] of Object.entries(zipContent.files)) { for (const [key, value] of Object.entries(zipContent.files)) {
if (!key.endsWith(".xml") && !key.endsWith(".rels")) { if (!key.endsWith(".xml") && !key.endsWith(".rels")) {
binaryContentMap.set(key, await value.async("nodebuffer")); binaryContentMap.set(key, await value.async("uint8array"));
continue; continue;
} }
@ -128,6 +129,7 @@ export const patchDocument = async (data: InputDataType, options: PatchDocumentO
patchText, patchText,
renderedParagraphs, renderedParagraphs,
context, context,
options.keepOriginalStyles,
); );
} }

View File

@ -43,6 +43,7 @@ export const replaceTokenInParagraphElement = ({
patchTextElement(paragraphElement.elements![run.index].elements![index], firstPart); patchTextElement(paragraphElement.elements![run.index].elements![index], firstPart);
replaceMode = ReplaceMode.MIDDLE; replaceMode = ReplaceMode.MIDDLE;
continue; continue;
/* c8 ignore next 2 */
} }
break; break;
case ReplaceMode.MIDDLE: case ReplaceMode.MIDDLE:
@ -59,6 +60,7 @@ export const replaceTokenInParagraphElement = ({
patchTextElement(paragraphElement.elements![run.index].elements![index], ""); patchTextElement(paragraphElement.elements![run.index].elements![index], "");
} }
break; break;
/* c8 ignore next */
default: default:
} }
} }

View File

@ -44,6 +44,28 @@ const MOCK_JSON = {
}, },
], ],
}, },
{
type: "element",
name: "w:p",
elements: [
{
type: "element",
name: "w:r",
elements: [
{
type: "element",
name: "w:rPr",
elements: [{ type: "element", name: "w:b", attributes: { "w:val": "1" } }],
},
{
type: "element",
name: "w:t",
elements: [{ type: "text", text: "What a {{bold}} text!" }],
},
],
},
],
},
], ],
}, },
], ],
@ -115,6 +137,93 @@ describe("replacer", () => {
expect(JSON.stringify(output)).to.contain("Delightful Header"); expect(JSON.stringify(output)).to.contain("Delightful Header");
}); });
it("should replace paragraph type keeping original styling if keepOriginalStyles is true", () => {
const output = replacer(
MOCK_JSON,
{
type: PatchType.PARAGRAPH,
children: [new TextRun("sweet")],
},
"{{bold}}",
[
{
text: "What a {{bold}} text!",
runs: [
{
text: "What a {{bold}} text!",
parts: [{ text: "What a {{bold}} text!", index: 1, start: 0, end: 21 }],
index: 0,
start: 0,
end: 21,
},
],
index: 0,
path: [0, 0, 1],
},
],
{
file: {} as unknown as File,
viewWrapper: {
Relationships: {},
} as unknown as IViewWrapper,
stack: [],
},
true,
);
expect(JSON.stringify(output)).to.contain("sweet");
expect(output.elements![0].elements![1].elements).toMatchObject([
{
type: "element",
name: "w:r",
elements: [
{
type: "element",
name: "w:rPr",
elements: [{ type: "element", name: "w:b", attributes: { "w:val": "1" } }],
},
{
type: "element",
name: "w:t",
elements: [{ type: "text", text: "What a " }],
},
],
},
{
type: "element",
name: "w:r",
elements: [
{
type: "element",
name: "w:rPr",
elements: [{ type: "element", name: "w:b", attributes: { "w:val": "1" } }],
},
{
type: "element",
name: "w:t",
elements: [{ type: "text", text: "sweet" }],
},
],
},
{
type: "element",
name: "w:r",
elements: [
{
type: "element",
name: "w:rPr",
elements: [{ type: "element", name: "w:b", attributes: { "w:val": "1" } }],
},
{
type: "element",
name: "w:t",
elements: [{ type: "text", text: " text!" }],
},
],
},
]);
});
it("should replace document type", () => { it("should replace document type", () => {
const output = replacer( const output = replacer(
MOCK_JSON, MOCK_JSON,

View File

@ -20,6 +20,7 @@ export const replacer = (
patchText: string, patchText: string,
renderedParagraphs: readonly IRenderedParagraphNode[], renderedParagraphs: readonly IRenderedParagraphNode[],
context: IContext, context: IContext,
keepOriginalStyles: boolean = false,
): Element => { ): Element => {
for (const renderedParagraph of renderedParagraphs) { for (const renderedParagraph of renderedParagraphs) {
const textJson = patch.children const textJson = patch.children
@ -47,9 +48,30 @@ export const replacer = (
const index = findRunElementIndexWithToken(paragraphElement, SPLIT_TOKEN); const index = findRunElementIndexWithToken(paragraphElement, SPLIT_TOKEN);
const { left, right } = splitRunElement(paragraphElement.elements![index], SPLIT_TOKEN); const runElementToBeReplaced = paragraphElement.elements![index];
const { left, right } = splitRunElement(runElementToBeReplaced, SPLIT_TOKEN);
let newRunElements = textJson;
let patchedRightElement = right;
if (keepOriginalStyles) {
const runElementNonTextualElements = runElementToBeReplaced.elements!.filter(
(e) => e.type === "element" && e.name !== "w:t",
);
newRunElements = textJson.map((e) => ({
...e,
elements: [...runElementNonTextualElements, ...e.elements!],
}));
patchedRightElement = {
...right,
elements: [...runElementNonTextualElements, ...right.elements!],
};
}
// eslint-disable-next-line functional/immutable-data // eslint-disable-next-line functional/immutable-data
paragraphElement.elements!.splice(index, 1, left, ...textJson, right); paragraphElement.elements!.splice(index, 1, left, ...newRunElements, patchedRightElement);
break; break;
} }
} }

View File

@ -9,7 +9,7 @@ export default defineConfig({
tsconfigPaths(), tsconfigPaths(),
dts(), dts(),
nodePolyfills({ nodePolyfills({
exclude: ["fs"], exclude: [],
globals: { globals: {
Buffer: false, Buffer: false,
}, },
@ -39,7 +39,7 @@ export default defineConfig({
} }
if (d === "es") { if (d === "es") {
return "index.js"; return "index.mjs";
} }
if (d === "iife") { if (d === "iife") {