web-doc 0.6.2 → 0.8.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 (201) hide show
  1. package/THIRD_PARTY_NOTICES.md +33 -1
  2. package/dist/adapters/docx-images.d.ts +13 -4
  3. package/dist/adapters/docx-images.js +3 -0
  4. package/dist/adapters/docx-paragraphs.d.ts +65 -0
  5. package/dist/adapters/docx-paragraphs.js +165 -0
  6. package/dist/adapters/docx-prepass.d.ts +17 -0
  7. package/dist/adapters/docx-prepass.js +230 -0
  8. package/dist/adapters/office.d.ts +31 -5
  9. package/dist/adapters/office.js +60 -29
  10. package/dist/adapters/pdf.d.ts +9 -0
  11. package/dist/adapters/pdf.js +10 -0
  12. package/dist/assets/pdfium/pdfium.wasm +0 -0
  13. package/dist/contracts.d.ts +53 -2
  14. package/dist/edit/ai/outline.d.ts +47 -0
  15. package/dist/edit/ai/outline.js +338 -0
  16. package/dist/edit/ai/targets.d.ts +16 -0
  17. package/dist/edit/ai/targets.js +309 -0
  18. package/dist/edit/ai/tools.d.ts +28 -0
  19. package/dist/edit/ai/tools.js +605 -0
  20. package/dist/edit/ai/types.d.ts +175 -0
  21. package/dist/edit/ai/types.js +1 -0
  22. package/dist/edit/assets.d.ts +23 -0
  23. package/dist/edit/assets.js +75 -0
  24. package/dist/edit/docx/elements.d.ts +6 -0
  25. package/dist/edit/docx/elements.js +90 -0
  26. package/dist/edit/docx/engine.d.ts +57 -0
  27. package/dist/edit/docx/engine.js +547 -0
  28. package/dist/edit/docx/handlers.d.ts +3 -0
  29. package/dist/edit/docx/handlers.js +15 -0
  30. package/dist/edit/docx/ids.d.ts +45 -0
  31. package/dist/edit/docx/ids.js +101 -0
  32. package/dist/edit/docx/model.d.ts +94 -0
  33. package/dist/edit/docx/model.js +350 -0
  34. package/dist/edit/docx/operations.d.ts +47 -0
  35. package/dist/edit/docx/operations.js +3 -0
  36. package/dist/edit/docx/provider.d.ts +16 -0
  37. package/dist/edit/docx/provider.js +37 -0
  38. package/dist/edit/docx/schemas.d.ts +6 -0
  39. package/dist/edit/docx/schemas.js +192 -0
  40. package/dist/edit/docx/session.d.ts +48 -0
  41. package/dist/edit/docx/session.js +467 -0
  42. package/dist/edit/docx/structure-ops.d.ts +6 -0
  43. package/dist/edit/docx/structure-ops.js +529 -0
  44. package/dist/edit/docx/style.d.ts +45 -0
  45. package/dist/edit/docx/style.js +375 -0
  46. package/dist/edit/docx/table-ops.d.ts +4 -0
  47. package/dist/edit/docx/table-ops.js +241 -0
  48. package/dist/edit/docx/text-ops.d.ts +40 -0
  49. package/dist/edit/docx/text-ops.js +491 -0
  50. package/dist/edit/docx/text.d.ts +34 -0
  51. package/dist/edit/docx/text.js +284 -0
  52. package/dist/edit/docx/tracked.d.ts +52 -0
  53. package/dist/edit/docx/tracked.js +347 -0
  54. package/dist/edit/docx/types.d.ts +206 -0
  55. package/dist/edit/docx/types.js +1 -0
  56. package/dist/edit/docx/write.d.ts +64 -0
  57. package/dist/edit/docx/write.js +375 -0
  58. package/dist/edit/engine.d.ts +128 -0
  59. package/dist/edit/engine.js +1 -0
  60. package/dist/edit/history.d.ts +77 -0
  61. package/dist/edit/history.js +123 -0
  62. package/dist/edit/ooxml/names.d.ts +20 -0
  63. package/dist/edit/ooxml/names.js +61 -0
  64. package/dist/edit/ooxml/opc.d.ts +50 -0
  65. package/dist/edit/ooxml/opc.js +150 -0
  66. package/dist/edit/ooxml/package.d.ts +82 -0
  67. package/dist/edit/ooxml/package.js +233 -0
  68. package/dist/edit/ooxml/patch.d.ts +51 -0
  69. package/dist/edit/ooxml/patch.js +250 -0
  70. package/dist/edit/ooxml/transaction.d.ts +39 -0
  71. package/dist/edit/ooxml/transaction.js +316 -0
  72. package/dist/edit/ooxml/worker.d.ts +8 -0
  73. package/dist/edit/ooxml/worker.js +12 -0
  74. package/dist/edit/ooxml/writer.d.ts +21 -0
  75. package/dist/edit/ooxml/writer.js +187 -0
  76. package/dist/edit/ooxml/xml.d.ts +74 -0
  77. package/dist/edit/ooxml/xml.js +451 -0
  78. package/dist/edit/ooxml/zip.d.ts +54 -0
  79. package/dist/edit/ooxml/zip.js +280 -0
  80. package/dist/edit/operations.d.ts +19 -0
  81. package/dist/edit/operations.js +137 -0
  82. package/dist/edit/pdf/engine/compact.d.ts +6 -0
  83. package/dist/edit/pdf/engine/compact.js +442 -0
  84. package/dist/edit/pdf/engine/document.d.ts +95 -0
  85. package/dist/edit/pdf/engine/document.js +868 -0
  86. package/dist/edit/pdf/engine/elements.d.ts +45 -0
  87. package/dist/edit/pdf/engine/elements.js +313 -0
  88. package/dist/edit/pdf/engine/existing-text.d.ts +4 -0
  89. package/dist/edit/pdf/engine/existing-text.js +424 -0
  90. package/dist/edit/pdf/engine/fonts.d.ts +78 -0
  91. package/dist/edit/pdf/engine/fonts.js +466 -0
  92. package/dist/edit/pdf/engine/geometry.d.ts +36 -0
  93. package/dist/edit/pdf/engine/geometry.js +97 -0
  94. package/dist/edit/pdf/engine/handler.d.ts +21 -0
  95. package/dist/edit/pdf/engine/handler.js +108 -0
  96. package/dist/edit/pdf/engine/images.d.ts +33 -0
  97. package/dist/edit/pdf/engine/images.js +188 -0
  98. package/dist/edit/pdf/engine/layout.d.ts +23 -0
  99. package/dist/edit/pdf/engine/layout.js +177 -0
  100. package/dist/edit/pdf/engine/operations.d.ts +67 -0
  101. package/dist/edit/pdf/engine/operations.js +3 -0
  102. package/dist/edit/pdf/engine/pages.d.ts +6 -0
  103. package/dist/edit/pdf/engine/pages.js +98 -0
  104. package/dist/edit/pdf/engine/pdfium.d.ts +195 -0
  105. package/dist/edit/pdf/engine/pdfium.js +249 -0
  106. package/dist/edit/pdf/engine/shapes.d.ts +4 -0
  107. package/dist/edit/pdf/engine/shapes.js +183 -0
  108. package/dist/edit/pdf/engine/tables.d.ts +44 -0
  109. package/dist/edit/pdf/engine/tables.js +315 -0
  110. package/dist/edit/pdf/engine/text-box.d.ts +71 -0
  111. package/dist/edit/pdf/engine/text-box.js +317 -0
  112. package/dist/edit/pdf/engine/text-layout.d.ts +28 -0
  113. package/dist/edit/pdf/engine/text-layout.js +67 -0
  114. package/dist/edit/pdf/engine/transform.d.ts +5 -0
  115. package/dist/edit/pdf/engine/transform.js +137 -0
  116. package/dist/edit/pdf/provider.d.ts +35 -0
  117. package/dist/edit/pdf/provider.js +92 -0
  118. package/dist/edit/pdf/range-map.d.ts +16 -0
  119. package/dist/edit/pdf/range-map.js +107 -0
  120. package/dist/edit/pdf/schemas.d.ts +11 -0
  121. package/dist/edit/pdf/schemas.js +291 -0
  122. package/dist/edit/pdf/selection.d.ts +5 -0
  123. package/dist/edit/pdf/selection.js +152 -0
  124. package/dist/edit/pdf/session.d.ts +63 -0
  125. package/dist/edit/pdf/session.js +312 -0
  126. package/dist/edit/pdf/types.d.ts +336 -0
  127. package/dist/edit/pdf/types.js +1 -0
  128. package/dist/edit/pptx/elements.d.ts +74 -0
  129. package/dist/edit/pptx/elements.js +301 -0
  130. package/dist/edit/pptx/engine.d.ts +37 -0
  131. package/dist/edit/pptx/engine.js +466 -0
  132. package/dist/edit/pptx/geometry.d.ts +72 -0
  133. package/dist/edit/pptx/geometry.js +193 -0
  134. package/dist/edit/pptx/handler.d.ts +7 -0
  135. package/dist/edit/pptx/handler.js +99 -0
  136. package/dist/edit/pptx/handlers.d.ts +3 -0
  137. package/dist/edit/pptx/handlers.js +21 -0
  138. package/dist/edit/pptx/image-table-ops.d.ts +5 -0
  139. package/dist/edit/pptx/image-table-ops.js +233 -0
  140. package/dist/edit/pptx/model.d.ts +69 -0
  141. package/dist/edit/pptx/model.js +170 -0
  142. package/dist/edit/pptx/operations.d.ts +50 -0
  143. package/dist/edit/pptx/operations.js +11 -0
  144. package/dist/edit/pptx/provider.d.ts +17 -0
  145. package/dist/edit/pptx/provider.js +40 -0
  146. package/dist/edit/pptx/schemas.d.ts +6 -0
  147. package/dist/edit/pptx/schemas.js +220 -0
  148. package/dist/edit/pptx/session.d.ts +54 -0
  149. package/dist/edit/pptx/session.js +145 -0
  150. package/dist/edit/pptx/shape-ops.d.ts +10 -0
  151. package/dist/edit/pptx/shape-ops.js +486 -0
  152. package/dist/edit/pptx/slide-ops.d.ts +6 -0
  153. package/dist/edit/pptx/slide-ops.js +353 -0
  154. package/dist/edit/pptx/style.d.ts +31 -0
  155. package/dist/edit/pptx/style.js +181 -0
  156. package/dist/edit/pptx/text-ops.d.ts +13 -0
  157. package/dist/edit/pptx/text-ops.js +267 -0
  158. package/dist/edit/pptx/text-write.d.ts +67 -0
  159. package/dist/edit/pptx/text-write.js +293 -0
  160. package/dist/edit/pptx/text.d.ts +41 -0
  161. package/dist/edit/pptx/text.js +99 -0
  162. package/dist/edit/pptx/types.d.ts +232 -0
  163. package/dist/edit/pptx/types.js +1 -0
  164. package/dist/edit/schema.d.ts +11 -0
  165. package/dist/edit/schema.js +275 -0
  166. package/dist/edit/session.d.ts +77 -0
  167. package/dist/edit/session.js +943 -0
  168. package/dist/edit/sessions.d.ts +8 -0
  169. package/dist/edit/sessions.js +1 -0
  170. package/dist/edit/types.d.ts +290 -0
  171. package/dist/edit/types.js +1 -0
  172. package/dist/edit/worker-engine.d.ts +31 -0
  173. package/dist/edit/worker-engine.js +91 -0
  174. package/dist/fonts/THIRD_PARTY_NOTICES.md +3 -0
  175. package/dist/fonts/manifest.json +6 -1
  176. package/dist/fonts/noto-sans-latin-cyrillic.ttf +0 -0
  177. package/dist/fonts.d.ts +2 -0
  178. package/dist/fonts.js +4 -0
  179. package/dist/fuzzy-alignment.d.ts +11 -4
  180. package/dist/fuzzy-alignment.js +3 -9
  181. package/dist/headless.d.ts +5 -1
  182. package/dist/headless.js +8 -1
  183. package/dist/index.d.ts +10 -2
  184. package/dist/index.js +16 -2
  185. package/dist/limits.js +6 -0
  186. package/dist/ooxml-edit-worker.d.ts +1 -0
  187. package/dist/ooxml-edit-worker.js +8 -0
  188. package/dist/pdf-edit-worker.d.ts +1 -0
  189. package/dist/pdf-edit-worker.js +35 -0
  190. package/dist/spreadsheet-viewport.d.ts +5 -0
  191. package/dist/spreadsheet-viewport.js +11 -0
  192. package/dist/ui.js +7 -0
  193. package/dist/viewer.d.ts +6 -0
  194. package/dist/viewer.js +293 -19
  195. package/dist/viewport.d.ts +13 -0
  196. package/dist/viewport.js +126 -1
  197. package/dist/worker-protocol.d.ts +39 -2
  198. package/dist/workers/fuzzy-search-worker.js +1 -1
  199. package/dist/workers/ooxml-edit-worker.js +9212 -0
  200. package/dist/workers/pdf-edit-worker.js +10847 -0
  201. package/package.json +3 -3
@@ -0,0 +1,206 @@
1
+ import type { ApplyOptions, BinaryData, EditColor, EditElement, EditReceipt, EditSessionBase, ReadOptions, ReadResult, SavedDocument, SaveOptions, TextRange } from "../types.js";
2
+ export type DocxElementKind = "paragraph" | "table" | "image" | "other";
3
+ export type DocxTextAlign = "left" | "center" | "right" | "justify";
4
+ /** Word's sixteen highlight colours (`w:highlight/@w:val`). */
5
+ export type DocxHighlight = "yellow" | "green" | "cyan" | "magenta" | "blue" | "red" | "darkBlue" | "darkCyan" | "darkGreen" | "darkMagenta" | "darkRed" | "darkYellow" | "darkGray" | "lightGray" | "black" | "white";
6
+ /** The style of a paragraph's first run with text, resolved through the style chain and the theme. */
7
+ export interface DocxTextStyle {
8
+ /** Theme fonts resolved ("minorHAnsi" becomes the minor Latin face). */
9
+ readonly fontFamily: string;
10
+ /** Points. */
11
+ readonly fontSize: number;
12
+ readonly bold: boolean;
13
+ readonly italic: boolean;
14
+ readonly underline: boolean;
15
+ /** `#RRGGBB`, `{ theme }` for a theme colour, "auto" for Word's automatic colour. */
16
+ readonly color: EditColor;
17
+ readonly highlight?: DocxHighlight;
18
+ }
19
+ export interface DocxParagraphSpacing {
20
+ /** Points before the paragraph; absent when inherited. */
21
+ readonly before?: number;
22
+ /** Points after the paragraph; absent when inherited. */
23
+ readonly after?: number;
24
+ /**
25
+ * Line spacing: points for `exact` and `atLeast`, a multiple of single
26
+ * spacing (1 = single, 1.5, 2) for `auto`; absent when inherited.
27
+ */
28
+ readonly line?: number;
29
+ readonly lineRule?: "auto" | "exact" | "atLeast";
30
+ }
31
+ export interface DocxParagraphStyle {
32
+ /** `w:pStyle`, when the paragraph names a style. */
33
+ readonly styleId?: string;
34
+ readonly align: DocxTextAlign;
35
+ readonly spacing: DocxParagraphSpacing;
36
+ /** `w:numPr`: the paragraph belongs to a list. */
37
+ readonly numbering?: {
38
+ readonly numId: number;
39
+ readonly level: number;
40
+ };
41
+ }
42
+ /** Why a paragraph's text cannot be edited in place. */
43
+ export type DocxReadOnlyReason = "tracked-changes" | "section-break" | "unsupported-content";
44
+ /**
45
+ * An element of the body story. Geometry comes from the renderer: `bounds`
46
+ * and `fragments` are the unions of the element's text runs per page, so
47
+ * an element whose pages the viewer has not laid out yet, or which draws
48
+ * no run (an empty paragraph), has `pageIndex` −1, empty `bounds` and an
49
+ * empty `fragments` list until a query names its page.
50
+ */
51
+ export interface DocxElement extends EditElement {
52
+ readonly kind: DocxElementKind;
53
+ /** Present for a paragraph: resolved style of its first run with text. */
54
+ readonly textStyle?: DocxTextStyle;
55
+ /** Present for a paragraph. */
56
+ readonly paragraphStyle?: DocxParagraphStyle;
57
+ /** Present for a table: cell text by row. */
58
+ readonly table?: {
59
+ readonly rows: readonly (readonly string[])[];
60
+ };
61
+ /** Present for a paragraph whose text cannot be edited in place. */
62
+ readonly readOnlyReason?: DocxReadOnlyReason;
63
+ /** Present for an inline picture: its declared extent in page space (CSS pixels). */
64
+ readonly imageSize?: {
65
+ readonly width: number;
66
+ readonly height: number;
67
+ };
68
+ }
69
+ export interface DocxTextStyleChange {
70
+ /** `w:rFonts` ascii and hAnsi faces. */
71
+ readonly fontFamily?: string;
72
+ /** 1–400 points. */
73
+ readonly fontSize?: number;
74
+ readonly bold?: boolean;
75
+ readonly italic?: boolean;
76
+ /** `w:u` single or none. */
77
+ readonly underline?: boolean;
78
+ /** `#RRGGBB` writes `w:color`; `{ theme }` writes the theme colour and its resolved value. */
79
+ readonly color?: EditColor;
80
+ /** `w:highlight`; "none" removes it. */
81
+ readonly highlight?: DocxHighlight | "none";
82
+ }
83
+ export interface DocxParagraphStyleChange {
84
+ readonly align?: DocxTextAlign;
85
+ /** Points; a field absent keeps its bytes. */
86
+ readonly spacing?: {
87
+ readonly before?: number;
88
+ readonly after?: number;
89
+ /** A multiple of single spacing, written with `w:lineRule="auto"`. */
90
+ readonly line?: number;
91
+ };
92
+ }
93
+ export interface DocxReplaceTextOperation {
94
+ readonly op: "replaceText";
95
+ /** A `paragraph` element. */
96
+ readonly target: string;
97
+ readonly text: string;
98
+ /** Both ends on the target; absent, the whole text; collapsed, an insertion. */
99
+ readonly range?: TextRange;
100
+ }
101
+ export interface DocxSetTextStyleOperation {
102
+ readonly op: "setTextStyle";
103
+ readonly target: string;
104
+ readonly range?: TextRange;
105
+ readonly style: DocxTextStyleChange;
106
+ }
107
+ export interface DocxSetParagraphStyleOperation {
108
+ readonly op: "setParagraphStyle";
109
+ readonly target: string;
110
+ readonly style: DocxParagraphStyleChange;
111
+ }
112
+ export interface DocxInsertParagraphOperation {
113
+ readonly op: "insertParagraph";
114
+ /** Exactly one of `before` and `after`: a paragraph or table id. */
115
+ readonly before?: string;
116
+ readonly after?: string;
117
+ readonly text: string;
118
+ readonly style?: DocxTextStyleChange;
119
+ }
120
+ export interface DocxDeleteElementOperation {
121
+ readonly op: "deleteElement";
122
+ /** A paragraph, a table or an inline picture. */
123
+ readonly target: string;
124
+ }
125
+ export interface DocxMoveElementOperation {
126
+ readonly op: "moveElement";
127
+ /** A paragraph or a table. */
128
+ readonly target: string;
129
+ /** Exactly one of `before` and `after`: a paragraph or table id of the same container. */
130
+ readonly before?: string;
131
+ readonly after?: string;
132
+ }
133
+ export interface DocxInsertTableOperation {
134
+ readonly op: "insertTable";
135
+ readonly before?: string;
136
+ readonly after?: string;
137
+ /** Cell text by row; every row has the same number of cells. 1–100 rows, 1–20 columns. */
138
+ readonly rows: readonly (readonly string[])[];
139
+ /** Relative column weights, one per column; equal when omitted. */
140
+ readonly columnWidths?: readonly number[];
141
+ }
142
+ export interface DocxSetTableCellOperation {
143
+ readonly op: "setTableCell";
144
+ /** A `table` element. */
145
+ readonly target: string;
146
+ readonly row: number;
147
+ readonly column: number;
148
+ /** The cell's new text; empty clears the cell. */
149
+ readonly text: string;
150
+ }
151
+ export interface DocxInsertImageOperation {
152
+ readonly op: "insertImage";
153
+ readonly before?: string;
154
+ readonly after?: string;
155
+ /** PNG or JPEG bytes; base64 over pure-JSON transports. */
156
+ readonly data: BinaryData;
157
+ readonly mimeType: "image/png" | "image/jpeg";
158
+ /** Points. */
159
+ readonly size: {
160
+ readonly width: number;
161
+ readonly height: number;
162
+ };
163
+ }
164
+ /** The DOCX operation union; operations are added as they ship. */
165
+ export type DocxOperation = DocxReplaceTextOperation | DocxSetTextStyleOperation | DocxSetParagraphStyleOperation | DocxInsertParagraphOperation | DocxDeleteElementOperation | DocxMoveElementOperation | DocxInsertTableOperation | DocxSetTableCellOperation | DocxInsertImageOperation;
166
+ /** DOCX saves have no fields of their own. */
167
+ export type DocxSaveOptions = SaveOptions;
168
+ /** One revision of a paragraph, as `getRevisions()` lists them in document order. */
169
+ export interface DocxRevision {
170
+ readonly kind: "ins" | "del" | "moveFrom" | "moveTo" | "rPrChange" | "pPrChange";
171
+ /** The `w:id`; -1 when the file leaves it out. */
172
+ readonly id: number;
173
+ readonly author?: string;
174
+ /** ISO 8601, as the file writes it. */
175
+ readonly date?: string;
176
+ /** Runs of the paragraph, its paragraph mark, or the paragraph's properties. */
177
+ readonly scope: "runs" | "mark" | "paragraph";
178
+ /** The inserted, deleted or restyled text; "\n" for a paragraph mark; absent for a property change. */
179
+ readonly text?: string;
180
+ }
181
+ /** An operation's fields without its `op`, as the typed methods take them. */
182
+ export type DocxFields<T extends DocxOperation> = T extends unknown ? Omit<T, "op"> : never;
183
+ export interface DocxEditSession extends EditSessionBase<DocxOperation, DocxElement> {
184
+ readonly format: "docx";
185
+ save(options?: DocxSaveOptions): Promise<SavedDocument>;
186
+ /** The tracked changes a paragraph holds, in document order; empty for other elements. */
187
+ getRevisions(elementId: string, options?: ReadOptions): Promise<ReadResult<DocxRevision>>;
188
+ /** Replaces the whole text of a paragraph, or the part a range covers. */
189
+ replaceText(fields: DocxFields<DocxReplaceTextOperation>, options?: ApplyOptions): Promise<EditReceipt>;
190
+ /** Changes run properties; unspecified ones keep their bytes. */
191
+ setTextStyle(fields: DocxFields<DocxSetTextStyleOperation>, options?: ApplyOptions): Promise<EditReceipt>;
192
+ /** Changes alignment and spacing of a paragraph. */
193
+ setParagraphStyle(fields: DocxFields<DocxSetParagraphStyleOperation>, options?: ApplyOptions): Promise<EditReceipt>;
194
+ /** Adds a paragraph next to another element; its id is in `createdIds`. */
195
+ insertParagraph(fields: DocxFields<DocxInsertParagraphOperation>, options?: ApplyOptions): Promise<EditReceipt>;
196
+ /** Removes a paragraph, a table or an inline picture. */
197
+ deleteElement(fields: DocxFields<DocxDeleteElementOperation>, options?: ApplyOptions): Promise<EditReceipt>;
198
+ /** Moves a paragraph or a table next to another element of the same container. */
199
+ moveElement(fields: DocxFields<DocxMoveElementOperation>, options?: ApplyOptions): Promise<EditReceipt>;
200
+ /** Adds a table with the given cell text. */
201
+ insertTable(fields: DocxFields<DocxInsertTableOperation>, options?: ApplyOptions): Promise<EditReceipt>;
202
+ /** Changes one cell's text. */
203
+ setTableCell(fields: DocxFields<DocxSetTableCellOperation>, options?: ApplyOptions): Promise<EditReceipt>;
204
+ /** Adds a paragraph holding an inline PNG or JPEG. */
205
+ insertImage(fields: DocxFields<DocxInsertImageOperation>, options?: ApplyOptions): Promise<EditReceipt>;
206
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,64 @@
1
+ import { type XmlPatch } from "../ooxml/patch.js";
2
+ import { type XmlElement, type XmlPart } from "../ooxml/xml.js";
3
+ import type { EditColor } from "../types.js";
4
+ import { type DocxStyles } from "./style.js";
5
+ import type { DocxParagraphStyleChange, DocxTextStyleChange } from "./types.js";
6
+ /** Children of `w:rPr` in schema order (CT_RPr). */
7
+ export declare const RPR_ORDER: string[];
8
+ /** Children of `w:pPr` in schema order (CT_PPr). */
9
+ export declare const PPR_ORDER: string[];
10
+ /** Why a text cannot be written, or undefined when it can. */
11
+ export declare function textProblem(text: string): string | undefined;
12
+ /** Why a value cannot be written as an attribute: text problems plus the breaks text may hold. */
13
+ export declare function attributeProblem(value: string): string | undefined;
14
+ export declare function colorProblem(color: unknown): string | undefined;
15
+ /** Text as the engine stores it: Windows and classic Mac line ends become "\n". */
16
+ export declare function normalizeText(text: string): string;
17
+ export declare function sliceOf(part: XmlPart, node: XmlElement | undefined): string;
18
+ export declare function escapeAttributeValue(value: string): string;
19
+ /** The content elements of a run for `text`: `w:t`, `w:tab`, `w:br`. */
20
+ export declare function runContentXml(text: string): string;
21
+ /** A run with `rPr` bytes (or "") and the given content; nothing for empty content. */
22
+ export declare function runXml(rPr: string, content: string): string;
23
+ /**
24
+ * Properties with changes merged in schema order: every child of `source`
25
+ * keeps its bytes unless `set` names its local name, in which case the
26
+ * replacement (or nothing, for `null`) takes its place; new children go
27
+ * where the schema puts them. Returns "" for no properties at all.
28
+ */
29
+ export declare function mergedProperties(part: XmlPart, source: XmlElement | undefined, tag: "w:rPr" | "w:pPr", order: readonly string[], set: ReadonlyMap<string, string | null>): string;
30
+ /**
31
+ * `w:rPr` bytes with a text style change applied. With `revision` (the
32
+ * attributes of a tracked change) the previous properties are kept in a
33
+ * `w:rPrChange`, so Word shows the change as a suggestion.
34
+ */
35
+ export declare function changedRunProperties(part: XmlPart, rPr: XmlElement | undefined, change: DocxTextStyleChange, styles: DocxStyles, revision?: string): string;
36
+ /** `w:color` for a colour: RGB, automatic, or a theme name with the theme's value. */
37
+ export declare function colorXml(color: EditColor, styles: DocxStyles): string;
38
+ /** The children of a properties element as bytes, the named ones left out. */
39
+ export declare function innerPropertiesXml(part: XmlPart, properties: XmlElement | undefined, without: readonly string[]): string;
40
+ /**
41
+ * `w:pPr` bytes with alignment and spacing changed, other children kept.
42
+ * With `revision` the previous paragraph properties are kept in a
43
+ * `w:pPrChange`.
44
+ */
45
+ export declare function changedParagraphProperties(part: XmlPart, pPr: XmlElement | undefined, change: DocxParagraphStyleChange, revision?: string): string;
46
+ export declare function alignValue(align: NonNullable<DocxParagraphStyleChange["align"]>): string;
47
+ /** `w:pPr` bytes with the paragraph mark's `w:rPr` changed (created when absent). */
48
+ export declare function paragraphMarkProperties(part: XmlPart, pPr: XmlElement | undefined, change: DocxTextStyleChange, styles: DocxStyles, revision?: string): string;
49
+ /** `w:pPr` bytes without the section properties, for a paragraph copied next to one that ends a section. */
50
+ export declare function paragraphPropertiesWithoutSection(part: XmlPart, pPr: XmlElement | undefined): string;
51
+ /**
52
+ * The start tag of a `w:p` with its id written: the paragraph's own
53
+ * attributes, `w14:paraId` and `w14:textId` replaced.
54
+ */
55
+ export declare function paragraphStartTag(source: XmlElement | undefined, id: string): string;
56
+ /** A whole paragraph from its start tag, property bytes and content. */
57
+ export declare function paragraphXml(source: XmlElement | undefined, id: string, pPr: string, content: string): string;
58
+ /**
59
+ * Patches that declare the `w14` namespace on the document root and list
60
+ * it as ignorable, when the root does not yet; nothing when it does.
61
+ */
62
+ export declare function namespacePatches(part: XmlPart): XmlPatch[];
63
+ /** Whether the paragraph already carries a `w14:paraId`. */
64
+ export declare function isAuthored(part: XmlPart, paragraph: XmlElement): boolean;
@@ -0,0 +1,375 @@
1
+ import { patches } from "../ooxml/patch.js";
2
+ import { escapeText } from "../ooxml/xml.js";
3
+ import { W14_NS, W_NS } from "./ids.js";
4
+ import { isThemeColorName } from "./style.js";
5
+ import { LINE_BREAK, PAGE_BREAK, TAB } from "./text.js";
6
+ /*
7
+ * Writing WordprocessingML: runs re-serialized from text and their
8
+ * properties, run and paragraph properties changed in schema order with
9
+ * every other child kept as its bytes, paragraph start tags carrying their
10
+ * `w14:paraId`, and the namespace declarations that attribute needs.
11
+ */
12
+ const MC_NS = "http://schemas.openxmlformats.org/markup-compatibility/2006";
13
+ /** Children of `w:rPr` in schema order (CT_RPr). */
14
+ export const RPR_ORDER = [
15
+ "rStyle",
16
+ "rFonts",
17
+ "b",
18
+ "bCs",
19
+ "i",
20
+ "iCs",
21
+ "caps",
22
+ "smallCaps",
23
+ "strike",
24
+ "dstrike",
25
+ "outline",
26
+ "shadow",
27
+ "emboss",
28
+ "imprint",
29
+ "noProof",
30
+ "snapToGrid",
31
+ "vanish",
32
+ "webHidden",
33
+ "color",
34
+ "spacing",
35
+ "w",
36
+ "kern",
37
+ "position",
38
+ "sz",
39
+ "szCs",
40
+ "highlight",
41
+ "u",
42
+ "effect",
43
+ "bdr",
44
+ "shd",
45
+ "fitText",
46
+ "vertAlign",
47
+ "rtl",
48
+ "cs",
49
+ "em",
50
+ "lang",
51
+ "eastAsianLayout",
52
+ "specVanish",
53
+ "oMath",
54
+ "rPrChange",
55
+ ];
56
+ /** Children of `w:pPr` in schema order (CT_PPr). */
57
+ export const PPR_ORDER = [
58
+ "pStyle",
59
+ "keepNext",
60
+ "keepLines",
61
+ "pageBreakBefore",
62
+ "framePr",
63
+ "widowControl",
64
+ "numPr",
65
+ "suppressLineNumbers",
66
+ "pBdr",
67
+ "shd",
68
+ "tabs",
69
+ "suppressAutoHyphens",
70
+ "kinsoku",
71
+ "wordWrap",
72
+ "overflowPunct",
73
+ "topLinePunct",
74
+ "autoSpaceDE",
75
+ "autoSpaceDN",
76
+ "bidi",
77
+ "adjustRightInd",
78
+ "snapToGrid",
79
+ "spacing",
80
+ "ind",
81
+ "contextualSpacing",
82
+ "mirrorIndents",
83
+ "suppressOverlap",
84
+ "jc",
85
+ "textDirection",
86
+ "textAlignment",
87
+ "textboxTightWrap",
88
+ "outlineLvl",
89
+ "divId",
90
+ "cnfStyle",
91
+ "rPr",
92
+ "sectPr",
93
+ "pPrChange",
94
+ ];
95
+ const FORBIDDEN = /[\u0000-\u0008\u000e-\u001f￾￿]/;
96
+ const LONE_SURROGATE = /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/;
97
+ /** Why a text cannot be written, or undefined when it can. */
98
+ export function textProblem(text) {
99
+ if (FORBIDDEN.test(text))
100
+ return "control characters XML cannot carry";
101
+ if (LONE_SURROGATE.test(text))
102
+ return "a lone surrogate";
103
+ return undefined;
104
+ }
105
+ /** Why a value cannot be written as an attribute: text problems plus the breaks text may hold. */
106
+ export function attributeProblem(value) {
107
+ if (/[\u000b\u000c]/.test(value))
108
+ return "line or page breaks an attribute cannot carry";
109
+ return textProblem(value);
110
+ }
111
+ export function colorProblem(color) {
112
+ if (typeof color === "string")
113
+ return color === "auto" || /^#[0-9A-Fa-f]{6}$/.test(color)
114
+ ? undefined
115
+ : "not #RRGGBB or auto";
116
+ if (!color || typeof color !== "object")
117
+ return "not a colour";
118
+ const { theme } = color;
119
+ return typeof theme === "string" && isThemeColorName(theme)
120
+ ? undefined
121
+ : "not a theme colour name";
122
+ }
123
+ /** Text as the engine stores it: Windows and classic Mac line ends become "\n". */
124
+ export function normalizeText(text) {
125
+ return text.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
126
+ }
127
+ export function sliceOf(part, node) {
128
+ return node ? part.text.slice(node.start, node.end) : "";
129
+ }
130
+ export function escapeAttributeValue(value) {
131
+ return value
132
+ .replaceAll("&", "&amp;")
133
+ .replaceAll("<", "&lt;")
134
+ .replaceAll('"', "&quot;")
135
+ .replaceAll("\t", "&#9;")
136
+ .replaceAll("\n", "&#10;")
137
+ .replaceAll("\r", "&#13;");
138
+ }
139
+ /** The content elements of a run for `text`: `w:t`, `w:tab`, `w:br`. */
140
+ export function runContentXml(text) {
141
+ let out = "";
142
+ let pending = "";
143
+ const flush = () => {
144
+ if (pending.length === 0)
145
+ return;
146
+ const preserve = /^\s|\s$/.test(pending) ? ' xml:space="preserve"' : "";
147
+ out += `<w:t${preserve}>${escapeText(pending)}</w:t>`;
148
+ pending = "";
149
+ };
150
+ for (const char of text) {
151
+ if (char === TAB) {
152
+ flush();
153
+ out += "<w:tab/>";
154
+ }
155
+ else if (char === LINE_BREAK) {
156
+ flush();
157
+ out += "<w:br/>";
158
+ }
159
+ else if (char === PAGE_BREAK) {
160
+ flush();
161
+ out += '<w:br w:type="page"/>';
162
+ }
163
+ else
164
+ pending += char;
165
+ }
166
+ flush();
167
+ return out;
168
+ }
169
+ /** A run with `rPr` bytes (or "") and the given content; nothing for empty content. */
170
+ export function runXml(rPr, content) {
171
+ return content.length === 0 ? "" : `<w:r>${rPr}${content}</w:r>`;
172
+ }
173
+ /**
174
+ * Properties with changes merged in schema order: every child of `source`
175
+ * keeps its bytes unless `set` names its local name, in which case the
176
+ * replacement (or nothing, for `null`) takes its place; new children go
177
+ * where the schema puts them. Returns "" for no properties at all.
178
+ */
179
+ export function mergedProperties(part, source, tag, order, set) {
180
+ const children = source
181
+ ? source.children.map((child) => ({
182
+ local: child.local,
183
+ xml: sliceOf(part, child),
184
+ }))
185
+ : [];
186
+ for (const [local, xml] of set) {
187
+ const index = children.findIndex((child) => child.local === local);
188
+ for (let at = children.length - 1; at >= 0; at -= 1)
189
+ if (children[at].local === local)
190
+ children.splice(at, 1);
191
+ if (xml === null)
192
+ continue;
193
+ let at = index;
194
+ if (at < 0) {
195
+ // Before the first child the schema places later; a child the
196
+ // schema list does not name (an extension) counts as later too.
197
+ const rank = order.indexOf(local);
198
+ at = children.findIndex((child) => {
199
+ const other = order.indexOf(child.local);
200
+ return other < 0 || other > rank;
201
+ });
202
+ if (at < 0)
203
+ at = children.length;
204
+ }
205
+ children.splice(at, 0, { local, xml });
206
+ }
207
+ const attributes = source
208
+ ? source.attributes.map((a) => ` ${a.name}="${a.rawValue}"`).join("")
209
+ : "";
210
+ const content = children.map((child) => child.xml).join("");
211
+ if (!content)
212
+ return source ? `<${tag}${attributes}/>` : "";
213
+ return `<${tag}${attributes}>${content}</${tag}>`;
214
+ }
215
+ function toggleXml(local, on) {
216
+ return on ? `<w:${local}/>` : `<w:${local} w:val="0"/>`;
217
+ }
218
+ /**
219
+ * `w:rPr` bytes with a text style change applied. With `revision` (the
220
+ * attributes of a tracked change) the previous properties are kept in a
221
+ * `w:rPrChange`, so Word shows the change as a suggestion.
222
+ */
223
+ export function changedRunProperties(part, rPr, change, styles, revision) {
224
+ const set = new Map();
225
+ if (revision !== undefined)
226
+ set.set("rPrChange", `<w:rPrChange${revision}>${wrapped("w:rPr", innerPropertiesXml(part, rPr, ["rPrChange"]))}</w:rPrChange>`);
227
+ if (change.bold !== undefined) {
228
+ set.set("b", toggleXml("b", change.bold));
229
+ set.set("bCs", toggleXml("bCs", change.bold));
230
+ }
231
+ if (change.italic !== undefined) {
232
+ set.set("i", toggleXml("i", change.italic));
233
+ set.set("iCs", toggleXml("iCs", change.italic));
234
+ }
235
+ if (change.underline !== undefined)
236
+ set.set("u", `<w:u w:val="${change.underline ? "single" : "none"}"/>`);
237
+ if (change.fontSize !== undefined) {
238
+ const half = String(Math.round(change.fontSize * 2));
239
+ set.set("sz", `<w:sz w:val="${half}"/>`);
240
+ set.set("szCs", `<w:szCs w:val="${half}"/>`);
241
+ }
242
+ if (change.color !== undefined)
243
+ set.set("color", colorXml(change.color, styles));
244
+ if (change.highlight !== undefined)
245
+ set.set("highlight", change.highlight === "none"
246
+ ? null
247
+ : `<w:highlight w:val="${change.highlight}"/>`);
248
+ if (change.fontFamily !== undefined)
249
+ set.set("rFonts", fontsXml(part, rPr, change.fontFamily));
250
+ return mergedProperties(part, rPr, "w:rPr", RPR_ORDER, set);
251
+ }
252
+ /** `w:color` for a colour: RGB, automatic, or a theme name with the theme's value. */
253
+ export function colorXml(color, styles) {
254
+ if (typeof color === "string") {
255
+ if (color === "auto")
256
+ return '<w:color w:val="auto"/>';
257
+ return `<w:color w:val="${color.slice(1).toUpperCase()}"/>`;
258
+ }
259
+ const value = styles.themeColor(color.theme) ?? "auto";
260
+ return `<w:color w:val="${value}" w:themeColor="${escapeAttributeValue(color.theme)}"/>`;
261
+ }
262
+ /** `w:rFonts` with the ASCII and high-ANSI faces set and their theme overrides dropped. */
263
+ function fontsXml(part, rPr, family) {
264
+ const existing = rPr?.children.find((child) => child.local === "rFonts" && child.namespace === W_NS);
265
+ const kept = (existing?.attributes ?? []).filter((attribute) => !["w:ascii", "w:hAnsi", "w:asciiTheme", "w:hAnsiTheme"].includes(attribute.name));
266
+ const face = escapeAttributeValue(family);
267
+ const rest = kept.map((a) => ` ${a.name}="${a.rawValue}"`).join("");
268
+ return `<w:rFonts w:ascii="${face}" w:hAnsi="${face}"${rest}/>`;
269
+ }
270
+ /** `inner` inside `tag`, self-closing when empty. */
271
+ function wrapped(tag, inner) {
272
+ return inner.length === 0 ? `<${tag}/>` : `<${tag}>${inner}</${tag}>`;
273
+ }
274
+ /** The children of a properties element as bytes, the named ones left out. */
275
+ export function innerPropertiesXml(part, properties, without) {
276
+ return (properties?.children ?? [])
277
+ .filter((child) => !without.includes(child.local))
278
+ .map((child) => sliceOf(part, child))
279
+ .join("");
280
+ }
281
+ /**
282
+ * `w:pPr` bytes with alignment and spacing changed, other children kept.
283
+ * With `revision` the previous paragraph properties are kept in a
284
+ * `w:pPrChange`.
285
+ */
286
+ export function changedParagraphProperties(part, pPr, change, revision) {
287
+ const set = new Map();
288
+ if (revision !== undefined)
289
+ set.set("pPrChange", `<w:pPrChange${revision}>${wrapped("w:pPr", innerPropertiesXml(part, pPr, ["rPr", "sectPr", "pPrChange"]))}</w:pPrChange>`);
290
+ if (change.align !== undefined)
291
+ set.set("jc", `<w:jc w:val="${alignValue(change.align)}"/>`);
292
+ if (change.spacing !== undefined) {
293
+ const existing = pPr?.children.find((child) => child.local === "spacing" && child.namespace === W_NS);
294
+ const attributes = new Map();
295
+ for (const attribute of existing?.attributes ?? [])
296
+ attributes.set(attribute.name, attribute.rawValue);
297
+ // Word prefers the line-based and automatic forms over w:before and
298
+ // w:after, so a set value drops them.
299
+ if (change.spacing.before !== undefined) {
300
+ attributes.set("w:before", String(Math.round(change.spacing.before * 20)));
301
+ attributes.delete("w:beforeLines");
302
+ attributes.delete("w:beforeAutospacing");
303
+ }
304
+ if (change.spacing.after !== undefined) {
305
+ attributes.set("w:after", String(Math.round(change.spacing.after * 20)));
306
+ attributes.delete("w:afterLines");
307
+ attributes.delete("w:afterAutospacing");
308
+ }
309
+ if (change.spacing.line !== undefined) {
310
+ attributes.set("w:line", String(Math.round(change.spacing.line * 240)));
311
+ attributes.set("w:lineRule", "auto");
312
+ }
313
+ set.set("spacing", `<w:spacing${[...attributes].map(([name, raw]) => ` ${name}="${raw}"`).join("")}/>`);
314
+ }
315
+ return mergedProperties(part, pPr, "w:pPr", PPR_ORDER, set);
316
+ }
317
+ export function alignValue(align) {
318
+ switch (align) {
319
+ case "center":
320
+ return "center";
321
+ case "right":
322
+ return "right";
323
+ case "justify":
324
+ return "both";
325
+ default:
326
+ return "left";
327
+ }
328
+ }
329
+ /** `w:pPr` bytes with the paragraph mark's `w:rPr` changed (created when absent). */
330
+ export function paragraphMarkProperties(part, pPr, change, styles, revision) {
331
+ const rPr = pPr?.children.find((child) => child.local === "rPr" && child.namespace === W_NS);
332
+ const changed = changedRunProperties(part, rPr, change, styles, revision);
333
+ return mergedProperties(part, pPr, "w:pPr", PPR_ORDER, new Map([["rPr", changed || null]]));
334
+ }
335
+ /** `w:pPr` bytes without the section properties, for a paragraph copied next to one that ends a section. */
336
+ export function paragraphPropertiesWithoutSection(part, pPr) {
337
+ if (!pPr)
338
+ return "";
339
+ return mergedProperties(part, pPr, "w:pPr", PPR_ORDER, new Map([["sectPr", null]]));
340
+ }
341
+ /**
342
+ * The start tag of a `w:p` with its id written: the paragraph's own
343
+ * attributes, `w14:paraId` and `w14:textId` replaced.
344
+ */
345
+ export function paragraphStartTag(source, id) {
346
+ const kept = (source?.attributes ?? []).filter((attribute) => attribute.name !== "w14:paraId" && attribute.name !== "w14:textId");
347
+ const rest = kept.map((a) => ` ${a.name}="${a.rawValue}"`).join("");
348
+ return `<w:p${rest} w14:paraId="${id}">`;
349
+ }
350
+ /** A whole paragraph from its start tag, property bytes and content. */
351
+ export function paragraphXml(source, id, pPr, content) {
352
+ return `${paragraphStartTag(source, id)}${pPr}${content}</w:p>`;
353
+ }
354
+ /**
355
+ * Patches that declare the `w14` namespace on the document root and list
356
+ * it as ignorable, when the root does not yet; nothing when it does.
357
+ */
358
+ export function namespacePatches(part) {
359
+ const root = part.root;
360
+ const items = [];
361
+ const has = (name) => root.attributes.find((attribute) => attribute.name === name)?.value;
362
+ if (has("xmlns:w14") === undefined)
363
+ items.push(patches.setAttribute(part, root, "xmlns:w14", W14_NS));
364
+ if (has("xmlns:mc") === undefined)
365
+ items.push(patches.setAttribute(part, root, "xmlns:mc", MC_NS));
366
+ const ignorable = has("mc:Ignorable");
367
+ const listed = ignorable ? ignorable.split(/\s+/).filter(Boolean) : [];
368
+ if (!listed.includes("w14"))
369
+ items.push(patches.setAttribute(part, root, "mc:Ignorable", [...listed, "w14"].join(" ")));
370
+ return items;
371
+ }
372
+ /** Whether the paragraph already carries a `w14:paraId`. */
373
+ export function isAuthored(part, paragraph) {
374
+ return part.attribute(paragraph, "w14:paraId") !== undefined;
375
+ }