@lotics/docx 0.1.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 (107) hide show
  1. package/package.json +40 -0
  2. package/src/fixtures/.gitkeep +0 -0
  3. package/src/fixtures/lotics_generated_contract.docx +0 -0
  4. package/src/fonts/bundled.ts +123 -0
  5. package/src/fonts/registry.test.ts +233 -0
  6. package/src/fonts/registry.ts +219 -0
  7. package/src/fonts/types.ts +83 -0
  8. package/src/index.ts +16 -0
  9. package/src/layout/engine.test.ts +430 -0
  10. package/src/layout/engine.ts +566 -0
  11. package/src/layout/page_geometry.ts +43 -0
  12. package/src/layout/types.ts +159 -0
  13. package/src/load.test.ts +144 -0
  14. package/src/load.ts +142 -0
  15. package/src/model/default_numbering.ts +101 -0
  16. package/src/model/default_styles.ts +201 -0
  17. package/src/model/numbering_table.ts +52 -0
  18. package/src/model/properties.ts +328 -0
  19. package/src/model/sections.ts +94 -0
  20. package/src/model/style_resolution.test.ts +219 -0
  21. package/src/model/style_resolution.ts +113 -0
  22. package/src/model/style_table.ts +22 -0
  23. package/src/model/theme.ts +156 -0
  24. package/src/model/types.ts +55 -0
  25. package/src/parse/drawing.ts +157 -0
  26. package/src/parse/font_table.ts +132 -0
  27. package/src/parse/footnotes.ts +60 -0
  28. package/src/parse/header_footer.test.ts +264 -0
  29. package/src/parse/header_footer.ts +66 -0
  30. package/src/parse/numbering.ts +187 -0
  31. package/src/parse/parser.ts +184 -0
  32. package/src/parse/relationships.ts +83 -0
  33. package/src/parse/sections.test.ts +192 -0
  34. package/src/parse/sections.ts +182 -0
  35. package/src/parse/styles.ts +149 -0
  36. package/src/parse/theme.test.ts +86 -0
  37. package/src/parse/theme.ts +112 -0
  38. package/src/pm/bubble_menu.ts +117 -0
  39. package/src/pm/commands.test.ts +185 -0
  40. package/src/pm/commands.ts +697 -0
  41. package/src/pm/commands_insert.test.ts +183 -0
  42. package/src/pm/docx_to_pm.test.ts +330 -0
  43. package/src/pm/docx_to_pm.ts +643 -0
  44. package/src/pm/drag_handle.ts +166 -0
  45. package/src/pm/format_painter.test.ts +91 -0
  46. package/src/pm/format_painter.ts +109 -0
  47. package/src/pm/header_footer_doc.ts +24 -0
  48. package/src/pm/hyperlinks.test.ts +234 -0
  49. package/src/pm/image_registry.test.ts +81 -0
  50. package/src/pm/image_registry.ts +100 -0
  51. package/src/pm/images.test.ts +257 -0
  52. package/src/pm/link_popover.ts +159 -0
  53. package/src/pm/mark_commands.ts +60 -0
  54. package/src/pm/marks.ts +169 -0
  55. package/src/pm/nodes.ts +258 -0
  56. package/src/pm/numbering.test.ts +210 -0
  57. package/src/pm/numbering_plugin.test.ts +71 -0
  58. package/src/pm/numbering_plugin.ts +96 -0
  59. package/src/pm/outline.ts +41 -0
  60. package/src/pm/page_break.test.ts +80 -0
  61. package/src/pm/page_layout.test.ts +87 -0
  62. package/src/pm/pagination_plugin.test.ts +155 -0
  63. package/src/pm/pagination_plugin.ts +590 -0
  64. package/src/pm/phase5.test.ts +271 -0
  65. package/src/pm/phase6.test.ts +215 -0
  66. package/src/pm/placeholder_plugin.ts +24 -0
  67. package/src/pm/plugins.ts +91 -0
  68. package/src/pm/pm_to_docx.ts +0 -0
  69. package/src/pm/roundtrip.test.ts +332 -0
  70. package/src/pm/schema.test.ts +188 -0
  71. package/src/pm/schema.ts +79 -0
  72. package/src/pm/search.ts +46 -0
  73. package/src/pm/table_attrs.ts +48 -0
  74. package/src/pm/table_borders.test.ts +117 -0
  75. package/src/pm/table_borders.ts +130 -0
  76. package/src/pm/table_convert.test.ts +221 -0
  77. package/src/pm/table_convert.ts +541 -0
  78. package/src/pm/table_decorations.ts +132 -0
  79. package/src/pm/table_handles.ts +163 -0
  80. package/src/pm/template_marker.ts +47 -0
  81. package/src/pm/template_plugin.ts +65 -0
  82. package/src/pm/templates.test.ts +162 -0
  83. package/src/render/clipboard.test.ts +115 -0
  84. package/src/render/clipboard.ts +200 -0
  85. package/src/render/editable_view.test.ts +173 -0
  86. package/src/render/footnotes_view.ts +94 -0
  87. package/src/render/header_footer_view.ts +95 -0
  88. package/src/render/link_mark_view.ts +26 -0
  89. package/src/render/media_resolver.ts +61 -0
  90. package/src/render/node_views.ts +296 -0
  91. package/src/render/numbering_counter.ts +149 -0
  92. package/src/render/page_chrome.test.ts +262 -0
  93. package/src/render/page_chrome.ts +343 -0
  94. package/src/render/page_styles.ts +234 -0
  95. package/src/render/paragraph_view.test.ts +162 -0
  96. package/src/render/paragraph_view.ts +141 -0
  97. package/src/render/ruler.ts +110 -0
  98. package/src/render/style_registry.ts +33 -0
  99. package/src/render/table_dom.test.ts +171 -0
  100. package/src/render/table_dom.ts +288 -0
  101. package/src/render/units.ts +18 -0
  102. package/src/render/view.test.ts +165 -0
  103. package/src/render/view.ts +607 -0
  104. package/src/roundtrip.test.ts +179 -0
  105. package/src/serialize/default_parts.ts +128 -0
  106. package/src/serialize/header_footer_pm.ts +82 -0
  107. package/src/serialize/serializer.ts +114 -0
@@ -0,0 +1,201 @@
1
+ import {
2
+ EMPTY_PARAGRAPH_PROPERTIES,
3
+ EMPTY_RUN_PROPERTIES,
4
+ type ParagraphProperties,
5
+ type RunProperties,
6
+ } from "./properties";
7
+ import type { Style, StyleTable } from "./style_table";
8
+
9
+ const PT_TO_HALF_POINTS = 2;
10
+ const PT_TO_TWIPS = 20;
11
+
12
+ function paraProps(
13
+ partial: Partial<ParagraphProperties> = {},
14
+ ): ParagraphProperties {
15
+ return { ...EMPTY_PARAGRAPH_PROPERTIES, ...partial };
16
+ }
17
+
18
+ function runProps(
19
+ partial: Partial<RunProperties> = {},
20
+ ): RunProperties {
21
+ return { ...EMPTY_RUN_PROPERTIES, ...partial };
22
+ }
23
+
24
+ function spacing(beforePt: number, afterPt: number, lineMult = 1.15) {
25
+ return {
26
+ before: beforePt * PT_TO_TWIPS,
27
+ after: afterPt * PT_TO_TWIPS,
28
+ line: Math.round(240 * lineMult),
29
+ lineRule: "auto" as const,
30
+ };
31
+ }
32
+
33
+ function defaultStyle(
34
+ id: string,
35
+ name: string,
36
+ paragraphProperties: ParagraphProperties | null,
37
+ runProperties: RunProperties | null,
38
+ basedOn: string | null = null,
39
+ ): [string, Style] {
40
+ return [
41
+ id,
42
+ {
43
+ id,
44
+ name,
45
+ type: "paragraph",
46
+ basedOn,
47
+ linkedStyleId: null,
48
+ paragraphProperties,
49
+ runProperties,
50
+ isDefault: false,
51
+ },
52
+ ];
53
+ }
54
+
55
+ function buildDefaultStyles(): ReadonlyMap<string, Style> {
56
+ const out = new Map<string, Style>();
57
+ out.set(...defaultStyle(
58
+ "Normal",
59
+ "Normal",
60
+ paraProps({ spacing: spacing(0, 8, 1.15) }),
61
+ runProps({ size: 11 * PT_TO_HALF_POINTS, color: "1F1F1F", fontAscii: "Calibri" }),
62
+ ));
63
+ out.set(...defaultStyle(
64
+ "Title",
65
+ "Title",
66
+ paraProps({ spacing: spacing(0, 4, 1.0) }),
67
+ runProps({
68
+ size: 28 * PT_TO_HALF_POINTS,
69
+ color: "1F1F1F",
70
+ bold: false,
71
+ fontAscii: "Calibri Light",
72
+ }),
73
+ "Normal",
74
+ ));
75
+ out.set(...defaultStyle(
76
+ "Subtitle",
77
+ "Subtitle",
78
+ paraProps({ spacing: spacing(0, 8, 1.15) }),
79
+ runProps({
80
+ size: 14 * PT_TO_HALF_POINTS,
81
+ color: "595959",
82
+ italic: true,
83
+ fontAscii: "Calibri",
84
+ }),
85
+ "Normal",
86
+ ));
87
+ out.set(...defaultStyle(
88
+ "Heading1",
89
+ "Heading 1",
90
+ paraProps({ spacing: spacing(12, 0, 1.15), keepNext: true, keepLines: true, outlineLevel: 0 }),
91
+ runProps({
92
+ size: 16 * PT_TO_HALF_POINTS,
93
+ color: "2E74B5",
94
+ bold: true,
95
+ fontAscii: "Calibri Light",
96
+ }),
97
+ "Normal",
98
+ ));
99
+ out.set(...defaultStyle(
100
+ "Heading2",
101
+ "Heading 2",
102
+ paraProps({ spacing: spacing(8, 0, 1.15), keepNext: true, keepLines: true, outlineLevel: 1 }),
103
+ runProps({
104
+ size: 13 * PT_TO_HALF_POINTS,
105
+ color: "2E74B5",
106
+ bold: true,
107
+ fontAscii: "Calibri Light",
108
+ }),
109
+ "Normal",
110
+ ));
111
+ out.set(...defaultStyle(
112
+ "Heading3",
113
+ "Heading 3",
114
+ paraProps({ spacing: spacing(8, 0, 1.15), keepNext: true, keepLines: true, outlineLevel: 2 }),
115
+ runProps({
116
+ size: 12 * PT_TO_HALF_POINTS,
117
+ color: "1F4E79",
118
+ bold: true,
119
+ fontAscii: "Calibri Light",
120
+ }),
121
+ "Normal",
122
+ ));
123
+ for (let level = 4; level <= 6; level++) {
124
+ out.set(...defaultStyle(
125
+ `Heading${level}`,
126
+ `Heading ${level}`,
127
+ paraProps({ spacing: spacing(6, 0, 1.15), keepNext: true, keepLines: true, outlineLevel: level - 1 }),
128
+ runProps({
129
+ size: 11 * PT_TO_HALF_POINTS,
130
+ color: "2E74B5",
131
+ bold: true,
132
+ italic: true,
133
+ fontAscii: "Calibri",
134
+ }),
135
+ "Normal",
136
+ ));
137
+ }
138
+ out.set(...defaultStyle(
139
+ "Quote",
140
+ "Quote",
141
+ paraProps({ spacing: spacing(8, 8, 1.15), indent: { left: 360, right: 360, firstLine: null, hanging: null } }),
142
+ runProps({
143
+ size: 11 * PT_TO_HALF_POINTS,
144
+ color: "404040",
145
+ italic: true,
146
+ fontAscii: "Calibri",
147
+ }),
148
+ "Normal",
149
+ ));
150
+ out.set(...defaultStyle(
151
+ "Code",
152
+ "Code",
153
+ paraProps({ spacing: spacing(0, 0, 1.0) }),
154
+ runProps({
155
+ size: 10 * PT_TO_HALF_POINTS,
156
+ color: "1F1F1F",
157
+ fontAscii: "Consolas",
158
+ }),
159
+ "Normal",
160
+ ));
161
+ return out;
162
+ }
163
+
164
+ const DEFAULT_STYLES = buildDefaultStyles();
165
+
166
+ export function buildDefaultStyleTable(): StyleTable {
167
+ return {
168
+ paragraphDefaults: paraProps({ spacing: spacing(0, 8, 1.15) }),
169
+ runDefaults: runProps({
170
+ size: 11 * PT_TO_HALF_POINTS,
171
+ color: "1F1F1F",
172
+ fontAscii: "Calibri",
173
+ }),
174
+ styles: DEFAULT_STYLES,
175
+ defaultParagraphStyleId: "Normal",
176
+ defaultCharacterStyleId: null,
177
+ };
178
+ }
179
+
180
+ /**
181
+ * Merge a parsed StyleTable with the built-in defaults. Parsed styles win;
182
+ * defaults fill in any missing IDs (e.g. when the docx omits styles.xml or
183
+ * doesn't define common headings).
184
+ */
185
+ export function withDefaultStyles(parsed: StyleTable | null): StyleTable {
186
+ const defaults = buildDefaultStyleTable();
187
+ if (!parsed) return defaults;
188
+ const merged = new Map(defaults.styles);
189
+ for (const [id, style] of parsed.styles) {
190
+ merged.set(id, style);
191
+ }
192
+ return {
193
+ paragraphDefaults: parsed.paragraphDefaults ?? defaults.paragraphDefaults,
194
+ runDefaults: parsed.runDefaults ?? defaults.runDefaults,
195
+ styles: merged,
196
+ defaultParagraphStyleId:
197
+ parsed.defaultParagraphStyleId ?? defaults.defaultParagraphStyleId,
198
+ defaultCharacterStyleId:
199
+ parsed.defaultCharacterStyleId ?? defaults.defaultCharacterStyleId,
200
+ };
201
+ }
@@ -0,0 +1,52 @@
1
+ import type { ParagraphProperties, RunProperties } from "./properties";
2
+
3
+ export type NumberFormat =
4
+ | "decimal"
5
+ | "decimalZero"
6
+ | "lowerLetter"
7
+ | "upperLetter"
8
+ | "lowerRoman"
9
+ | "upperRoman"
10
+ | "bullet"
11
+ | "none";
12
+
13
+ export type Justification = "left" | "center" | "right";
14
+
15
+ export type NumberingLevel = {
16
+ ilvl: number;
17
+ start: number;
18
+ numFmt: NumberFormat;
19
+ lvlText: string;
20
+ lvlJc: Justification;
21
+ lvlRestart: number | null;
22
+ paragraphProperties: ParagraphProperties | null;
23
+ runProperties: RunProperties | null;
24
+ suff: "tab" | "space" | "nothing";
25
+ };
26
+
27
+ export type AbstractNum = {
28
+ abstractNumId: number;
29
+ levels: ReadonlyMap<number, NumberingLevel>;
30
+ };
31
+
32
+ export type LvlOverride = {
33
+ ilvl: number;
34
+ startOverride: number | null;
35
+ level: NumberingLevel | null;
36
+ };
37
+
38
+ export type Num = {
39
+ numId: number;
40
+ abstractNumId: number;
41
+ overrides: ReadonlyMap<number, LvlOverride>;
42
+ };
43
+
44
+ export type NumberingTable = {
45
+ abstractNums: ReadonlyMap<number, AbstractNum>;
46
+ nums: ReadonlyMap<number, Num>;
47
+ };
48
+
49
+ export const EMPTY_NUMBERING_TABLE: NumberingTable = {
50
+ abstractNums: new Map(),
51
+ nums: new Map(),
52
+ };
@@ -0,0 +1,328 @@
1
+ import {
2
+ getAttr,
3
+ getChildren,
4
+ getTagName,
5
+ type XmlElement,
6
+ } from "@lotics/ooxml/xml";
7
+
8
+ export type Alignment =
9
+ | "left"
10
+ | "center"
11
+ | "right"
12
+ | "both"
13
+ | "start"
14
+ | "end"
15
+ | "distribute";
16
+
17
+ export type LineRule = "auto" | "exact" | "atLeast";
18
+
19
+ export type UnderlineStyle =
20
+ | "single"
21
+ | "double"
22
+ | "thick"
23
+ | "dotted"
24
+ | "dash"
25
+ | "dashLong"
26
+ | "dotDash"
27
+ | "dotDotDash"
28
+ | "wave"
29
+ | "wavyDouble"
30
+ | "wavyHeavy"
31
+ | "words"
32
+ | "none";
33
+
34
+ export type VerticalAlignRun = "baseline" | "superscript" | "subscript";
35
+
36
+ export type ParagraphProperties = {
37
+ styleId: string | null;
38
+ alignment: Alignment | null;
39
+ indent: {
40
+ left: number | null;
41
+ right: number | null;
42
+ firstLine: number | null;
43
+ hanging: number | null;
44
+ } | null;
45
+ spacing: {
46
+ before: number | null;
47
+ after: number | null;
48
+ line: number | null;
49
+ lineRule: LineRule | null;
50
+ } | null;
51
+ numbering: { numId: number; ilvl: number } | null;
52
+ pageBreakBefore: boolean | null;
53
+ keepLines: boolean | null;
54
+ keepNext: boolean | null;
55
+ widowControl: boolean | null;
56
+ outlineLevel: number | null;
57
+ };
58
+
59
+ export type RunProperties = {
60
+ styleId: string | null;
61
+ bold: boolean | null;
62
+ italic: boolean | null;
63
+ underline: UnderlineStyle | null;
64
+ strike: boolean | null;
65
+ doubleStrike: boolean | null;
66
+ caps: boolean | null;
67
+ smallCaps: boolean | null;
68
+ color: string | null;
69
+ themeColor: string | null;
70
+ themeTint: string | null;
71
+ themeShade: string | null;
72
+ highlight: string | null;
73
+ size: number | null;
74
+ fontAscii: string | null;
75
+ fontHAnsi: string | null;
76
+ fontEastAsia: string | null;
77
+ fontComplexScript: string | null;
78
+ vertAlign: VerticalAlignRun | null;
79
+ };
80
+
81
+ export const EMPTY_PARAGRAPH_PROPERTIES: ParagraphProperties = {
82
+ styleId: null,
83
+ alignment: null,
84
+ indent: null,
85
+ spacing: null,
86
+ numbering: null,
87
+ pageBreakBefore: null,
88
+ keepLines: null,
89
+ keepNext: null,
90
+ widowControl: null,
91
+ outlineLevel: null,
92
+ };
93
+
94
+ export const EMPTY_RUN_PROPERTIES: RunProperties = {
95
+ styleId: null,
96
+ bold: null,
97
+ italic: null,
98
+ underline: null,
99
+ strike: null,
100
+ doubleStrike: null,
101
+ caps: null,
102
+ smallCaps: null,
103
+ color: null,
104
+ themeColor: null,
105
+ themeTint: null,
106
+ themeShade: null,
107
+ highlight: null,
108
+ size: null,
109
+ fontAscii: null,
110
+ fontHAnsi: null,
111
+ fontEastAsia: null,
112
+ fontComplexScript: null,
113
+ vertAlign: null,
114
+ };
115
+
116
+ function readToggle(el: XmlElement): boolean {
117
+ const val = getAttr(el, "w:val");
118
+ if (val === undefined) return true;
119
+ if (val === "0" || val === "false") return false;
120
+ return true;
121
+ }
122
+
123
+ function parseIntOrNull(value: string | undefined): number | null {
124
+ if (value === undefined) return null;
125
+ const n = Number.parseInt(value, 10);
126
+ return Number.isFinite(n) ? n : null;
127
+ }
128
+
129
+ function isAlignment(value: string): value is Alignment {
130
+ return (
131
+ value === "left" ||
132
+ value === "center" ||
133
+ value === "right" ||
134
+ value === "both" ||
135
+ value === "start" ||
136
+ value === "end" ||
137
+ value === "distribute"
138
+ );
139
+ }
140
+
141
+ function isLineRule(value: string): value is LineRule {
142
+ return value === "auto" || value === "exact" || value === "atLeast";
143
+ }
144
+
145
+ function isUnderlineStyle(value: string): value is UnderlineStyle {
146
+ return (
147
+ value === "single" ||
148
+ value === "double" ||
149
+ value === "thick" ||
150
+ value === "dotted" ||
151
+ value === "dash" ||
152
+ value === "dashLong" ||
153
+ value === "dotDash" ||
154
+ value === "dotDotDash" ||
155
+ value === "wave" ||
156
+ value === "wavyDouble" ||
157
+ value === "wavyHeavy" ||
158
+ value === "words" ||
159
+ value === "none"
160
+ );
161
+ }
162
+
163
+ function isVertAlignRun(value: string): value is VerticalAlignRun {
164
+ return value === "baseline" || value === "superscript" || value === "subscript";
165
+ }
166
+
167
+ export function extractParagraphProperties(
168
+ pPrChildren: readonly XmlElement[],
169
+ ): ParagraphProperties {
170
+ const out: ParagraphProperties = { ...EMPTY_PARAGRAPH_PROPERTIES };
171
+
172
+ for (const el of pPrChildren) {
173
+ const tag = getTagName(el);
174
+ switch (tag) {
175
+ case "w:pStyle": {
176
+ const v = getAttr(el, "w:val");
177
+ if (v) out.styleId = v;
178
+ break;
179
+ }
180
+ case "w:jc": {
181
+ const v = getAttr(el, "w:val");
182
+ if (v && isAlignment(v)) out.alignment = v;
183
+ break;
184
+ }
185
+ case "w:ind": {
186
+ const left = parseIntOrNull(getAttr(el, "w:left") ?? getAttr(el, "w:start"));
187
+ const right = parseIntOrNull(getAttr(el, "w:right") ?? getAttr(el, "w:end"));
188
+ const firstLine = parseIntOrNull(getAttr(el, "w:firstLine"));
189
+ const hanging = parseIntOrNull(getAttr(el, "w:hanging"));
190
+ if (left !== null || right !== null || firstLine !== null || hanging !== null) {
191
+ out.indent = { left, right, firstLine, hanging };
192
+ }
193
+ break;
194
+ }
195
+ case "w:spacing": {
196
+ const before = parseIntOrNull(getAttr(el, "w:before"));
197
+ const after = parseIntOrNull(getAttr(el, "w:after"));
198
+ const line = parseIntOrNull(getAttr(el, "w:line"));
199
+ const lineRuleStr = getAttr(el, "w:lineRule");
200
+ const lineRule =
201
+ lineRuleStr && isLineRule(lineRuleStr) ? lineRuleStr : null;
202
+ if (
203
+ before !== null ||
204
+ after !== null ||
205
+ line !== null ||
206
+ lineRule !== null
207
+ ) {
208
+ out.spacing = { before, after, line, lineRule };
209
+ }
210
+ break;
211
+ }
212
+ case "w:numPr": {
213
+ let numId: number | null = null;
214
+ let ilvl = 0;
215
+ for (const child of getChildren(el)) {
216
+ const childTag = getTagName(child);
217
+ if (childTag === "w:numId") {
218
+ numId = parseIntOrNull(getAttr(child, "w:val"));
219
+ } else if (childTag === "w:ilvl") {
220
+ const lvl = parseIntOrNull(getAttr(child, "w:val"));
221
+ if (lvl !== null) ilvl = lvl;
222
+ }
223
+ }
224
+ if (numId !== null) out.numbering = { numId, ilvl };
225
+ break;
226
+ }
227
+ case "w:pageBreakBefore":
228
+ out.pageBreakBefore = readToggle(el);
229
+ break;
230
+ case "w:keepLines":
231
+ out.keepLines = readToggle(el);
232
+ break;
233
+ case "w:keepNext":
234
+ out.keepNext = readToggle(el);
235
+ break;
236
+ case "w:widowControl":
237
+ out.widowControl = readToggle(el);
238
+ break;
239
+ case "w:outlineLvl": {
240
+ const v = parseIntOrNull(getAttr(el, "w:val"));
241
+ if (v !== null) out.outlineLevel = v;
242
+ break;
243
+ }
244
+ }
245
+ }
246
+
247
+ return out;
248
+ }
249
+
250
+ export function extractRunProperties(
251
+ rPrChildren: readonly XmlElement[],
252
+ ): RunProperties {
253
+ const out: RunProperties = { ...EMPTY_RUN_PROPERTIES };
254
+
255
+ for (const el of rPrChildren) {
256
+ const tag = getTagName(el);
257
+ switch (tag) {
258
+ case "w:rStyle": {
259
+ const v = getAttr(el, "w:val");
260
+ if (v) out.styleId = v;
261
+ break;
262
+ }
263
+ case "w:b":
264
+ out.bold = readToggle(el);
265
+ break;
266
+ case "w:i":
267
+ out.italic = readToggle(el);
268
+ break;
269
+ case "w:u": {
270
+ const v = getAttr(el, "w:val");
271
+ if (v && isUnderlineStyle(v)) out.underline = v;
272
+ else if (v === undefined) out.underline = "single";
273
+ break;
274
+ }
275
+ case "w:strike":
276
+ out.strike = readToggle(el);
277
+ break;
278
+ case "w:dstrike":
279
+ out.doubleStrike = readToggle(el);
280
+ break;
281
+ case "w:caps":
282
+ out.caps = readToggle(el);
283
+ break;
284
+ case "w:smallCaps":
285
+ out.smallCaps = readToggle(el);
286
+ break;
287
+ case "w:color": {
288
+ const v = getAttr(el, "w:val");
289
+ if (v && v !== "auto") out.color = v;
290
+ const themeColor = getAttr(el, "w:themeColor");
291
+ if (themeColor) out.themeColor = themeColor;
292
+ const themeTint = getAttr(el, "w:themeTint");
293
+ if (themeTint) out.themeTint = themeTint;
294
+ const themeShade = getAttr(el, "w:themeShade");
295
+ if (themeShade) out.themeShade = themeShade;
296
+ break;
297
+ }
298
+ case "w:highlight": {
299
+ const v = getAttr(el, "w:val");
300
+ if (v) out.highlight = v;
301
+ break;
302
+ }
303
+ case "w:sz": {
304
+ const v = parseIntOrNull(getAttr(el, "w:val"));
305
+ if (v !== null) out.size = v;
306
+ break;
307
+ }
308
+ case "w:rFonts": {
309
+ const ascii = getAttr(el, "w:ascii");
310
+ const hAnsi = getAttr(el, "w:hAnsi");
311
+ const eastAsia = getAttr(el, "w:eastAsia");
312
+ const cs = getAttr(el, "w:cs");
313
+ if (ascii) out.fontAscii = ascii;
314
+ if (hAnsi) out.fontHAnsi = hAnsi;
315
+ if (eastAsia) out.fontEastAsia = eastAsia;
316
+ if (cs) out.fontComplexScript = cs;
317
+ break;
318
+ }
319
+ case "w:vertAlign": {
320
+ const v = getAttr(el, "w:val");
321
+ if (v && isVertAlignRun(v)) out.vertAlign = v;
322
+ break;
323
+ }
324
+ }
325
+ }
326
+
327
+ return out;
328
+ }
@@ -0,0 +1,94 @@
1
+ export type Orientation = "portrait" | "landscape";
2
+
3
+ export type PageSize = {
4
+ width: number;
5
+ height: number;
6
+ orientation: Orientation;
7
+ };
8
+
9
+ export type PageMargins = {
10
+ top: number;
11
+ right: number;
12
+ bottom: number;
13
+ left: number;
14
+ header: number;
15
+ footer: number;
16
+ gutter: number;
17
+ };
18
+
19
+ export type Column = {
20
+ width: number | null;
21
+ space: number | null;
22
+ };
23
+
24
+ export type Columns = {
25
+ count: number;
26
+ space: number | null;
27
+ equalWidth: boolean;
28
+ separator: boolean;
29
+ columns: readonly Column[];
30
+ };
31
+
32
+ export type SectionType =
33
+ | "continuous"
34
+ | "evenPage"
35
+ | "nextColumn"
36
+ | "nextPage"
37
+ | "oddPage";
38
+
39
+ export type HeaderFooterType = "default" | "first" | "even";
40
+
41
+ export type HeaderFooterRef = {
42
+ type: HeaderFooterType;
43
+ relationshipId: string;
44
+ };
45
+
46
+ export type SectionProperties = {
47
+ type: SectionType;
48
+ pageSize: PageSize;
49
+ margins: PageMargins;
50
+ columns: Columns;
51
+ titlePage: boolean;
52
+ headerRefs: readonly HeaderFooterRef[];
53
+ footerRefs: readonly HeaderFooterRef[];
54
+ };
55
+
56
+ export type Section = {
57
+ properties: SectionProperties;
58
+ blockStartIndex: number;
59
+ blockEndIndex: number;
60
+ };
61
+
62
+ export const DEFAULT_PAGE_SIZE: PageSize = {
63
+ width: 12240,
64
+ height: 15840,
65
+ orientation: "portrait",
66
+ };
67
+
68
+ export const DEFAULT_PAGE_MARGINS: PageMargins = {
69
+ top: 1440,
70
+ right: 1440,
71
+ bottom: 1440,
72
+ left: 1440,
73
+ header: 720,
74
+ footer: 720,
75
+ gutter: 0,
76
+ };
77
+
78
+ export const DEFAULT_COLUMNS: Columns = {
79
+ count: 1,
80
+ space: null,
81
+ equalWidth: true,
82
+ separator: false,
83
+ columns: [],
84
+ };
85
+
86
+ export const DEFAULT_SECTION_PROPERTIES: SectionProperties = {
87
+ type: "nextPage",
88
+ pageSize: DEFAULT_PAGE_SIZE,
89
+ margins: DEFAULT_PAGE_MARGINS,
90
+ columns: DEFAULT_COLUMNS,
91
+ titlePage: false,
92
+ headerRefs: [],
93
+ footerRefs: [],
94
+ };