docx 7.1.0 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +3 -2
  2. package/build/file/core-properties/properties.d.ts +1 -1
  3. package/build/file/document/body/section-properties/properties/doc-grid.d.ts +11 -1
  4. package/build/file/document/body/section-properties/properties/doc-grid.spec.d.ts +1 -0
  5. package/build/file/document/body/section-properties/properties/index.d.ts +1 -0
  6. package/build/file/document/body/section-properties/properties/page-text-direction.d.ts +8 -0
  7. package/build/file/document/body/section-properties/properties/page-text-direction.spec.d.ts +1 -0
  8. package/build/file/document/body/section-properties/section-properties.d.ts +3 -1
  9. package/build/file/document/document-background/document-background.d.ts +1 -1
  10. package/build/file/numbering/level.spec.d.ts +1 -0
  11. package/build/file/paragraph/links/bookmark-attributes.d.ts +2 -2
  12. package/build/file/paragraph/links/bookmark.d.ts +2 -2
  13. package/build/file/paragraph/run/properties.d.ts +7 -0
  14. package/build/index.js +1 -1
  15. package/package.json +14 -7
  16. package/src/export/packer/next-compiler.ts +2 -7
  17. package/src/export/packer/packer.spec.ts +1 -1
  18. package/src/file/core-properties/properties.spec.ts +1 -1
  19. package/src/file/core-properties/properties.ts +2 -2
  20. package/src/file/document/body/section-properties/properties/doc-grid.spec.ts +30 -0
  21. package/src/file/document/body/section-properties/properties/doc-grid.ts +15 -1
  22. package/src/file/document/body/section-properties/properties/index.ts +1 -0
  23. package/src/file/document/body/section-properties/properties/page-text-direction.spec.ts +22 -0
  24. package/src/file/document/body/section-properties/properties/page-text-direction.ts +22 -0
  25. package/src/file/document/body/section-properties/section-properties.spec.ts +18 -1
  26. package/src/file/document/body/section-properties/section-properties.ts +9 -2
  27. package/src/file/document/document-background/document-background.spec.ts +2 -4
  28. package/src/file/document/document-background/document-background.ts +2 -2
  29. package/src/file/document/document.spec.ts +1 -3
  30. package/src/file/file.ts +1 -1
  31. package/src/file/numbering/level.spec.ts +25 -0
  32. package/src/file/numbering/level.ts +6 -0
  33. package/src/file/paragraph/formatting/unordered-list.spec.ts +7 -7
  34. package/src/file/paragraph/formatting/unordered-list.ts +7 -0
  35. package/src/file/paragraph/links/bookmark-attributes.ts +2 -2
  36. package/src/file/paragraph/links/bookmark.spec.ts +1 -1
  37. package/src/file/paragraph/links/bookmark.ts +36 -4
  38. package/src/file/paragraph/paragraph.spec.ts +5 -2
  39. package/src/file/paragraph/properties.spec.ts +45 -11
  40. package/src/file/paragraph/properties.ts +1 -1
  41. package/src/file/paragraph/run/properties.ts +23 -0
  42. package/src/file/paragraph/run/run.spec.ts +60 -0
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "docx",
3
- "version": "7.1.0",
3
+ "version": "7.3.0",
4
4
  "description": "Easily generate .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.",
5
5
  "main": "build/index.js",
6
6
  "scripts": {
7
7
  "pretest": "rimraf ./build",
8
- "test": "mocha -r ts-node/register -r tsconfig-paths/register \"src/**/*.ts\"",
8
+ "test": "cross-env TS_NODE_PROJECT=\"tsconfig.spec.json\" mocha -r ts-node/register -r tsconfig-paths/register \"src/**/*.ts\"",
9
9
  "test.coverage": "nyc npm test",
10
10
  "test.watch": "npm test -- --watch",
11
11
  "prepublishOnly": "npm run build --production",
@@ -16,9 +16,10 @@
16
16
  "typedoc": "rimraf ./build && typedoc src/index.ts --tsconfig tsconfig.typedoc.json",
17
17
  "style": "prettier -l \"src/**/*.ts\"",
18
18
  "style.fix": "npm run style -- --write",
19
- "fix-types": "ts-node scripts/types-absolute-fixer.ts",
19
+ "fix-types": "ts-node --skip-project scripts/types-absolute-fixer.ts",
20
20
  "e2e": "ts-node scripts/e2e.ts",
21
21
  "serve.docs": "cd docs && docsify serve",
22
+ "extract": "ts-node --project tsconfig.spec.json scripts/extract-document.ts",
22
23
  "ts-node": "ts-node --skip-project"
23
24
  },
24
25
  "pre-commit": [
@@ -48,7 +49,7 @@
48
49
  ],
49
50
  "types": "./build/index.d.ts",
50
51
  "dependencies": {
51
- "@types/node": "^16.0.0",
52
+ "@types/node": "^17.0.0",
52
53
  "jszip": "^3.1.5",
53
54
  "nanoid": "^3.1.20",
54
55
  "xml": "^1.0.1",
@@ -62,12 +63,17 @@
62
63
  "homepage": "https://github.com/dolanmiu/docx#readme",
63
64
  "devDependencies": {
64
65
  "@types/chai": "^4.2.15",
66
+ "@types/glob": "^7.1.4",
65
67
  "@types/mocha": "^9.0.0",
68
+ "@types/prompt": "^1.1.1",
66
69
  "@types/request-promise": "^4.1.42",
70
+ "@types/shelljs": "^0.8.9",
67
71
  "@types/sinon": "^10.0.0",
72
+ "@types/unzipper": "^0.10.4",
68
73
  "@types/webpack": "^5.0.0",
69
74
  "buffer": "^6.0.3",
70
75
  "chai": "^3.5.0",
76
+ "cross-env": "^7.0.3",
71
77
  "docsify-cli": "^4.3.0",
72
78
  "glob": "^7.1.2",
73
79
  "jszip": "^3.1.5",
@@ -82,15 +88,16 @@
82
88
  "request-promise": "^4.2.2",
83
89
  "rimraf": "^3.0.2",
84
90
  "shelljs": "^0.8.4",
85
- "sinon": "^11.0.0",
91
+ "sinon": "^12.0.1",
86
92
  "stream-browserify": "^3.0.0",
87
93
  "ts-loader": "^9.0.0",
88
- "ts-node": "^9.0.0",
94
+ "ts-node": "^10.2.1",
89
95
  "tsconfig-paths": "^3.9.0",
90
96
  "tslint": "^6.1.3",
91
97
  "tslint-immutable": "^6.0.1",
92
98
  "typedoc": "^0.22.3",
93
- "typescript": "4.4.3",
99
+ "typescript": "4.5.4",
100
+ "unzipper": "^0.10.11",
94
101
  "webpack": "^5.28.0",
95
102
  "webpack-cli": "^4.6.0"
96
103
  },
@@ -44,14 +44,9 @@ export class Compiler {
44
44
  public compile(file: File, prettifyXml?: boolean): JSZip {
45
45
  const zip = new JSZip();
46
46
  const xmlifiedFileMapping = this.xmlifyFile(file, prettifyXml);
47
+ const map = new Map<string, IXmlifyedFile | IXmlifyedFile[]>(Object.entries(xmlifiedFileMapping));
47
48
 
48
- for (const key in xmlifiedFileMapping) {
49
- if (!xmlifiedFileMapping[key]) {
50
- continue;
51
- }
52
-
53
- const obj = xmlifiedFileMapping[key] as IXmlifyedFile | IXmlifyedFile[];
54
-
49
+ for (const [, obj] of map) {
55
50
  if (Array.isArray(obj)) {
56
51
  for (const subFile of obj) {
57
52
  zip.file(subFile.path, subFile.data);
@@ -12,7 +12,7 @@ describe("Packer", () => {
12
12
  beforeEach(() => {
13
13
  file = new File({
14
14
  creator: "Dolan Miu",
15
- revision: "1",
15
+ revision: 1,
16
16
  lastModifiedBy: "Dolan Miu",
17
17
  sections: [
18
18
  {
@@ -38,7 +38,7 @@ describe("Properties", () => {
38
38
  keywords: "test docx",
39
39
  description: "testing document",
40
40
  lastModifiedBy: "the author",
41
- revision: "123",
41
+ revision: 123,
42
42
  });
43
43
  const tree = new Formatter().format(properties);
44
44
  expect(Object.keys(tree)).to.deep.equal(["cp:coreProperties"]);
@@ -17,7 +17,7 @@ export interface IPropertiesOptions {
17
17
  readonly keywords?: string;
18
18
  readonly description?: string;
19
19
  readonly lastModifiedBy?: string;
20
- readonly revision?: string;
20
+ readonly revision?: number;
21
21
  readonly externalStyles?: string;
22
22
  readonly styles?: IStylesOptions;
23
23
  readonly numbering?: INumberingOptions;
@@ -89,7 +89,7 @@ export class CoreProperties extends XmlComponent {
89
89
  this.root.push(new StringContainer("cp:lastModifiedBy", options.lastModifiedBy));
90
90
  }
91
91
  if (options.revision) {
92
- this.root.push(new StringContainer("cp:revision", options.revision));
92
+ this.root.push(new StringContainer("cp:revision", String(options.revision)));
93
93
  }
94
94
  this.root.push(new TimestampElement("dcterms:created"));
95
95
  this.root.push(new TimestampElement("dcterms:modified"));
@@ -0,0 +1,30 @@
1
+ import { expect } from "chai";
2
+
3
+ import { Formatter } from "export/formatter";
4
+
5
+ import { DocumentGrid, DocumentGridType } from ".";
6
+
7
+ describe("DocumentGrid", () => {
8
+ describe("#constructor()", () => {
9
+ it("should create documentGrid with specified linePitch", () => {
10
+ const docGrid = new DocumentGrid(360);
11
+ const tree = new Formatter().format(docGrid);
12
+
13
+ expect(tree["w:docGrid"]).to.deep.equal({ _attr: { "w:linePitch": 360 } });
14
+ });
15
+
16
+ it("should create documentGrid with specified linePitch and type", () => {
17
+ const docGrid = new DocumentGrid(360, undefined, DocumentGridType.LINES);
18
+ const tree = new Formatter().format(docGrid);
19
+
20
+ expect(tree["w:docGrid"]).to.deep.equal({ _attr: { "w:linePitch": 360, "w:type": "lines" } });
21
+ });
22
+
23
+ it("should create documentGrid with specified linePitch,charSpace and type", () => {
24
+ const docGrid = new DocumentGrid(346, -1541, DocumentGridType.LINES_AND_CHARS);
25
+ const tree = new Formatter().format(docGrid);
26
+
27
+ expect(tree["w:docGrid"]).to.deep.equal({ _attr: { "w:linePitch": 346, "w:charSpace": -1541, "w:type": "linesAndChars" } });
28
+ });
29
+ });
30
+ });
@@ -16,22 +16,36 @@ import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
16
16
  // <xsd:attribute name="linePitch" type="ST_DecimalNumber"/>
17
17
  // <xsd:attribute name="charSpace" type="ST_DecimalNumber"/>
18
18
  // </xsd:complexType>
19
+
20
+ export enum DocumentGridType {
21
+ DEFAULT = "default",
22
+ LINES = "lines",
23
+ LINES_AND_CHARS = "linesAndChars",
24
+ SNAP_TO_CHARS = "snapToChars",
25
+ }
19
26
  export interface IDocGridAttributesProperties {
27
+ readonly type?: DocumentGridType;
20
28
  readonly linePitch?: number;
29
+ readonly charSpace?: number;
21
30
  }
22
31
 
23
32
  export class DocGridAttributes extends XmlAttributeComponent<IDocGridAttributesProperties> {
24
33
  protected readonly xmlKeys = {
34
+ type: "w:type",
25
35
  linePitch: "w:linePitch",
36
+ charSpace: "w:charSpace",
26
37
  };
27
38
  }
28
39
 
29
40
  export class DocumentGrid extends XmlComponent {
30
- constructor(linePitch: number) {
41
+ constructor(linePitch: number, charSpace?: number, type?: DocumentGridType) {
31
42
  super("w:docGrid");
43
+
32
44
  this.root.push(
33
45
  new DocGridAttributes({
46
+ type: type,
34
47
  linePitch: decimalNumber(linePitch),
48
+ charSpace: charSpace ? decimalNumber(charSpace) : undefined,
35
49
  }),
36
50
  );
37
51
  }
@@ -7,6 +7,7 @@ export * from "./page-number";
7
7
  export * from "./page-borders";
8
8
  export * from "./page-margin";
9
9
  export * from "./page-borders";
10
+ export * from "./page-text-direction";
10
11
  export * from "./line-number";
11
12
  export * from "./section-type";
12
13
  export * from "./header-footer-reference";
@@ -0,0 +1,22 @@
1
+ import { expect } from "chai";
2
+
3
+ import { Formatter } from "export/formatter";
4
+ import { PageTextDirection, PageTextDirectionType } from "./page-text-direction";
5
+
6
+ describe("PageTextDirection", () => {
7
+ describe("#constructor()", () => {
8
+ it("should set the direction of the text flow to top-to-bottom-right-to-left", () => {
9
+ const textDirection = new PageTextDirection(PageTextDirectionType.TOP_TO_BOTTOM_RIGHT_TO_LEFT);
10
+
11
+ const tree = new Formatter().format(textDirection);
12
+
13
+ expect(tree).to.deep.equal({
14
+ "w:textDirection": {
15
+ _attr: {
16
+ "w:val": "tbRl",
17
+ },
18
+ },
19
+ });
20
+ });
21
+ });
22
+ });
@@ -0,0 +1,22 @@
1
+ import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
2
+
3
+ export enum PageTextDirectionType {
4
+ LEFT_TO_RIGHT_TOP_TO_BOTTOM = "lrTb",
5
+ TOP_TO_BOTTOM_RIGHT_TO_LEFT = "tbRl",
6
+ }
7
+
8
+ class PageTextDirectionAttributes extends XmlAttributeComponent<{ readonly val: PageTextDirectionType }> {
9
+ protected readonly xmlKeys = { val: "w:val" };
10
+ }
11
+
12
+ export class PageTextDirection extends XmlComponent {
13
+ constructor(value: PageTextDirectionType) {
14
+ super("w:textDirection");
15
+
16
+ this.root.push(
17
+ new PageTextDirectionAttributes({
18
+ val: value,
19
+ }),
20
+ );
21
+ }
22
+ }
@@ -9,8 +9,10 @@ import { NumberFormat } from "file/shared/number-format";
9
9
  import { VerticalAlign } from "file/vertical-align";
10
10
 
11
11
  import { PageOrientation } from "./properties";
12
+ import { DocumentGridType } from "./properties/doc-grid";
12
13
  import { LineNumberRestartFormat } from "./properties/line-number";
13
14
  import { PageBorderOffsetFrom } from "./properties/page-borders";
15
+ import { PageTextDirectionType } from "./properties/page-text-direction";
14
16
  import { SectionType } from "./properties/section-type";
15
17
  import { sectionMarginDefaults, sectionPageSizeDefaults, SectionProperties } from "./section-properties";
16
18
 
@@ -63,6 +65,7 @@ describe("SectionProperties", () => {
63
65
  },
64
66
  grid: {
65
67
  linePitch: convertInchesToTwip(0.25),
68
+ type: DocumentGridType.LINES,
66
69
  },
67
70
  headerWrapperGroup: {
68
71
  default: new HeaderWrapper(media, 100),
@@ -99,7 +102,7 @@ describe("SectionProperties", () => {
99
102
  expect(tree["w:sectPr"][5]).to.deep.equal({ "w:cols": { _attr: { "w:space": 208, "w:sep": true, "w:num": 2 } } });
100
103
  expect(tree["w:sectPr"][6]).to.deep.equal({ "w:vAlign": { _attr: { "w:val": "top" } } });
101
104
  expect(tree["w:sectPr"][7]).to.deep.equal({ "w:titlePg": {} });
102
- expect(tree["w:sectPr"][8]).to.deep.equal({ "w:docGrid": { _attr: { "w:linePitch": 360 } } });
105
+ expect(tree["w:sectPr"][8]).to.deep.equal({ "w:docGrid": { _attr: { "w:linePitch": 360, "w:type": "lines" } } });
103
106
  });
104
107
 
105
108
  it("should create section properties with no options", () => {
@@ -258,5 +261,19 @@ describe("SectionProperties", () => {
258
261
  "w:lnNumType": { _attr: { "w:countBy": 2, "w:distance": 4, "w:restart": "continuous", "w:start": 2 } },
259
262
  });
260
263
  });
264
+
265
+ it("should create section properties with text flow direction", () => {
266
+ const properties = new SectionProperties({
267
+ page: {
268
+ textDirection: PageTextDirectionType.TOP_TO_BOTTOM_RIGHT_TO_LEFT,
269
+ },
270
+ });
271
+ const tree = new Formatter().format(properties);
272
+ expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
273
+ const type = tree["w:sectPr"].find((item) => item["w:textDirection"] !== undefined);
274
+ expect(type).to.deep.equal({
275
+ "w:textDirection": { _attr: { "w:val": "tbRl" } },
276
+ });
277
+ });
261
278
  });
262
279
  });
@@ -15,6 +15,7 @@ import { IPageBordersOptions, PageBorders } from "./properties/page-borders";
15
15
  import { IPageMarginAttributes, PageMargin } from "./properties/page-margin";
16
16
  import { IPageNumberTypeAttributes, PageNumberType } from "./properties/page-number";
17
17
  import { IPageSizeAttributes, PageOrientation, PageSize } from "./properties/page-size";
18
+ import { PageTextDirection, PageTextDirectionType } from "./properties/page-text-direction";
18
19
  import { SectionType, Type } from "./properties/section-type";
19
20
 
20
21
  export interface IHeaderFooterGroup<T> {
@@ -29,6 +30,7 @@ export interface ISectionPropertiesOptions {
29
30
  readonly margin?: IPageMarginAttributes;
30
31
  readonly pageNumbers?: IPageNumberTypeAttributes;
31
32
  readonly borders?: IPageBordersOptions;
33
+ readonly textDirection?: PageTextDirectionType;
32
34
  };
33
35
  readonly grid?: IDocGridAttributesProperties;
34
36
  readonly headerWrapperGroup?: IHeaderFooterGroup<HeaderWrapper>;
@@ -108,8 +110,9 @@ export class SectionProperties extends XmlComponent {
108
110
  } = {},
109
111
  pageNumbers = {},
110
112
  borders,
113
+ textDirection,
111
114
  } = {},
112
- grid: { linePitch = 360 } = {},
115
+ grid: { linePitch = 360, charSpace, type: gridType } = {},
113
116
  headerWrapperGroup = {},
114
117
  footerWrapperGroup = {},
115
118
  lineNumbers,
@@ -152,7 +155,11 @@ export class SectionProperties extends XmlComponent {
152
155
  this.root.push(new OnOffElement("w:titlePg", titlePage));
153
156
  }
154
157
 
155
- this.root.push(new DocumentGrid(linePitch));
158
+ if (textDirection) {
159
+ this.root.push(new PageTextDirection(textDirection));
160
+ }
161
+
162
+ this.root.push(new DocumentGrid(linePitch, charSpace, gridType));
156
163
  }
157
164
 
158
165
  private addHeaderFooterGroup(
@@ -6,14 +6,12 @@ import { DocumentBackground } from "./document-background";
6
6
 
7
7
  describe("DocumentBackground", () => {
8
8
  describe("#constructor()", () => {
9
- it("should create a DocumentBackground with no options and set color to auto", () => {
9
+ it("should create a DocumentBackground with no options", () => {
10
10
  const documentBackground = new DocumentBackground({});
11
11
  const tree = new Formatter().format(documentBackground);
12
12
  expect(tree).to.deep.equal({
13
13
  "w:background": {
14
- _attr: {
15
- "w:color": "FFFFFF",
16
- },
14
+ _attr: {},
17
15
  },
18
16
  });
19
17
  });
@@ -26,7 +26,7 @@ import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
26
26
  // </xsd:simpleType>
27
27
 
28
28
  export class DocumentBackgroundAttributes extends XmlAttributeComponent<{
29
- readonly color: string;
29
+ readonly color?: string;
30
30
  readonly themeColor?: string;
31
31
  readonly themeShade?: string;
32
32
  readonly themeTint?: string;
@@ -68,7 +68,7 @@ export class DocumentBackground extends XmlComponent {
68
68
 
69
69
  this.root.push(
70
70
  new DocumentBackgroundAttributes({
71
- color: hexColorValue(options.color ? options.color : "FFFFFF"),
71
+ color: options.color === undefined ? undefined : hexColorValue(options.color),
72
72
  themeColor: options.themeColor,
73
73
  themeShade: options.themeShade === undefined ? undefined : uCharHexNumber(options.themeShade),
74
74
  themeTint: options.themeTint === undefined ? undefined : uCharHexNumber(options.themeTint),
@@ -40,9 +40,7 @@ describe("Document", () => {
40
40
  },
41
41
  {
42
42
  "w:background": {
43
- _attr: {
44
- "w:color": "FFFFFF",
45
- },
43
+ _attr: {},
46
44
  },
47
45
  },
48
46
  { "w:body": {} },
package/src/file/file.ts CHANGED
@@ -57,7 +57,7 @@ export class File {
57
57
  this.coreProperties = new CoreProperties({
58
58
  ...options,
59
59
  creator: options.creator ?? "Un-named",
60
- revision: options.revision ?? "1",
60
+ revision: options.revision ?? 1,
61
61
  lastModifiedBy: options.lastModifiedBy ?? "Un-named",
62
62
  });
63
63
 
@@ -0,0 +1,25 @@
1
+ import { expect } from "chai";
2
+
3
+ import { LevelFormat, LevelSuffix } from ".";
4
+ import { AlignmentType } from "..";
5
+
6
+ import { Level } from "./level";
7
+
8
+ describe("Level", () => {
9
+ describe("#constructor", () => {
10
+ it("should throw an error if level exceeds 9", () => {
11
+ expect(
12
+ () =>
13
+ new Level({
14
+ level: 10,
15
+ format: LevelFormat.BULLET,
16
+ text: "test",
17
+ alignment: AlignmentType.BOTH,
18
+ start: 3,
19
+ style: { run: {}, paragraph: {} },
20
+ suffix: LevelSuffix.SPACE,
21
+ }),
22
+ ).to.throw();
23
+ });
24
+ });
25
+ });
@@ -161,6 +161,12 @@ export class LevelBase extends XmlComponent {
161
161
  this.root.push(this.paragraphProperties);
162
162
  this.root.push(this.runProperties);
163
163
 
164
+ if (level > 9) {
165
+ throw new Error(
166
+ "Level cannot be greater than 9. Read more here: https://answers.microsoft.com/en-us/msoffice/forum/all/does-word-support-more-than-9-list-levels/d130fdcd-1781-446d-8c84-c6c79124e4d7",
167
+ );
168
+ }
169
+
164
170
  this.root.push(
165
171
  new LevelAttributes({
166
172
  ilvl: decimalNumber(level),
@@ -5,21 +5,17 @@ import { Formatter } from "export/formatter";
5
5
  import { NumberProperties } from "./unordered-list";
6
6
 
7
7
  describe("NumberProperties", () => {
8
- let numberProperties: NumberProperties;
9
-
10
- beforeEach(() => {
11
- numberProperties = new NumberProperties(5, 10);
12
- });
13
-
14
8
  describe("#constructor()", () => {
15
9
  it("should create a Number Properties with correct root key", () => {
10
+ const numberProperties = new NumberProperties(5, 9);
11
+
16
12
  const tree = new Formatter().format(numberProperties);
17
13
  expect(tree).to.deep.equal({
18
14
  "w:numPr": [
19
15
  {
20
16
  "w:ilvl": {
21
17
  _attr: {
22
- "w:val": 10,
18
+ "w:val": 9,
23
19
  },
24
20
  },
25
21
  },
@@ -33,5 +29,9 @@ describe("NumberProperties", () => {
33
29
  ],
34
30
  });
35
31
  });
32
+
33
+ it("should throw an error if level exceeds 9", () => {
34
+ expect(() => new NumberProperties(5, 10)).to.throw();
35
+ });
36
36
  });
37
37
  });
@@ -11,6 +11,13 @@ export class NumberProperties extends XmlComponent {
11
11
  class IndentLevel extends XmlComponent {
12
12
  constructor(level: number) {
13
13
  super("w:ilvl");
14
+
15
+ if (level > 9) {
16
+ throw new Error(
17
+ "Level cannot be greater than 9. Read more here: https://answers.microsoft.com/en-us/msoffice/forum/all/does-word-support-more-than-9-list-levels/d130fdcd-1781-446d-8c84-c6c79124e4d7",
18
+ );
19
+ }
20
+
14
21
  this.root.push(
15
22
  new Attributes({
16
23
  val: level,
@@ -1,7 +1,7 @@
1
1
  import { XmlAttributeComponent } from "file/xml-components";
2
2
 
3
3
  export class BookmarkStartAttributes extends XmlAttributeComponent<{
4
- readonly id: string;
4
+ readonly id: number;
5
5
  readonly name: string;
6
6
  }> {
7
7
  protected readonly xmlKeys = {
@@ -11,7 +11,7 @@ export class BookmarkStartAttributes extends XmlAttributeComponent<{
11
11
  }
12
12
 
13
13
  export class BookmarkEndAttributes extends XmlAttributeComponent<{
14
- readonly id: string;
14
+ readonly id: number;
15
15
  }> {
16
16
  protected readonly xmlKeys = {
17
17
  id: "w:id",
@@ -36,6 +36,6 @@ describe("Bookmark", () => {
36
36
 
37
37
  it("should create a bookmark with the correct attributes on the bookmark end element", () => {
38
38
  const newJson = Utility.jsonify(bookmark);
39
- expect(newJson.end.root[0].root.id).to.be.a("string");
39
+ expect(newJson.end.root[0].root.id).to.be.a("number");
40
40
  });
41
41
  });
@@ -1,5 +1,5 @@
1
1
  // http://officeopenxml.com/WPbookmark.php
2
- import { uniqueId } from "convenience-functions";
2
+ import { uniqueNumericId } from "convenience-functions";
3
3
  import { XmlComponent } from "file/xml-components";
4
4
 
5
5
  import { ParagraphChild } from "../paragraph";
@@ -11,7 +11,7 @@ export class Bookmark {
11
11
  public readonly end: BookmarkEnd;
12
12
 
13
13
  constructor(options: { readonly id: string; readonly children: ParagraphChild[] }) {
14
- const linkId = uniqueId();
14
+ const linkId = uniqueNumericId();
15
15
 
16
16
  this.start = new BookmarkStart(options.id, linkId);
17
17
  this.children = options.children;
@@ -19,8 +19,40 @@ export class Bookmark {
19
19
  }
20
20
  }
21
21
 
22
+ // <xsd:element name="bookmarkStart" type="CT_Bookmark"/>
23
+ // <xsd:element name="bookmarkEnd" type="CT_MarkupRange"/>
24
+
25
+ // <xsd:complexType name="CT_Bookmark">
26
+ // <xsd:complexContent>
27
+ // <xsd:extension base="CT_BookmarkRange">
28
+ // <xsd:attribute name="name" type="s:ST_String" use="required"/>
29
+ // </xsd:extension>
30
+ // </xsd:complexContent>
31
+ // </xsd:complexType>
32
+
33
+ // <xsd:complexType name="CT_BookmarkRange">
34
+ // <xsd:complexContent>
35
+ // <xsd:extension base="CT_MarkupRange">
36
+ // <xsd:attribute name="colFirst" type="ST_DecimalNumber" use="optional"/>
37
+ // <xsd:attribute name="colLast" type="ST_DecimalNumber" use="optional"/>
38
+ // </xsd:extension>
39
+ // </xsd:complexContent>
40
+ // </xsd:complexType>
41
+
42
+ // <xsd:complexType name="CT_MarkupRange">
43
+ // <xsd:complexContent>
44
+ // <xsd:extension base="CT_Markup">
45
+ // <xsd:attribute name="displacedByCustomXml" type="ST_DisplacedByCustomXml" use="optional"/>
46
+ // </xsd:extension>
47
+ // </xsd:complexContent>
48
+ // </xsd:complexType>
49
+
50
+ // <xsd:complexType name="CT_Markup">
51
+ // <xsd:attribute name="id" type="ST_DecimalNumber" use="required"/>
52
+ // </xsd:complexType>
53
+
22
54
  export class BookmarkStart extends XmlComponent {
23
- constructor(id: string, linkId: string) {
55
+ constructor(id: string, linkId: number) {
24
56
  super("w:bookmarkStart");
25
57
 
26
58
  const attributes = new BookmarkStartAttributes({
@@ -32,7 +64,7 @@ export class BookmarkStart extends XmlComponent {
32
64
  }
33
65
 
34
66
  export class BookmarkEnd extends XmlComponent {
35
- constructor(linkId: string) {
67
+ constructor(linkId: number) {
36
68
  super("w:bookmarkEnd");
37
69
 
38
70
  const attributes = new BookmarkEndAttributes({
@@ -22,6 +22,9 @@ describe("Paragraph", () => {
22
22
  stub(convenienceFunctions, "uniqueId").callsFake(() => {
23
23
  return "test-unique-id";
24
24
  });
25
+ stub(convenienceFunctions, "uniqueNumericId").callsFake(() => {
26
+ return -101;
27
+ });
25
28
  });
26
29
 
27
30
  after(() => {
@@ -716,7 +719,7 @@ describe("Paragraph", () => {
716
719
  {
717
720
  "w:bookmarkStart": {
718
721
  _attr: {
719
- "w:id": "test-unique-id",
722
+ "w:id": -101,
720
723
  "w:name": "test-id",
721
724
  },
722
725
  },
@@ -738,7 +741,7 @@ describe("Paragraph", () => {
738
741
  {
739
742
  "w:bookmarkEnd": {
740
743
  _attr: {
741
- "w:id": "test-unique-id",
744
+ "w:id": -101,
742
745
  },
743
746
  },
744
747
  },