@stll/folio-core 0.15.1 → 0.15.2

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 (36) hide show
  1. package/dist/docx/commentParser.js +30 -2
  2. package/dist/docx/drawingIdNormalization.d.ts +5 -0
  3. package/dist/docx/drawingIdNormalization.js +35 -0
  4. package/dist/docx/drawingUtils.d.ts +1 -1
  5. package/dist/docx/drawingUtils.js +111 -27
  6. package/dist/docx/headerFooterVerbatim.d.ts +2 -1
  7. package/dist/docx/headerFooterVerbatim.js +6 -1
  8. package/dist/docx/paragraphParser.js +5 -2
  9. package/dist/docx/paragraphTraversal.d.ts +5 -3
  10. package/dist/docx/paragraphTraversal.js +20 -13
  11. package/dist/docx/parser.js +11 -1
  12. package/dist/docx/parserEnums.d.ts +4 -1
  13. package/dist/docx/parserEnums.js +5 -2
  14. package/dist/docx/runConsolidator.js +0 -1
  15. package/dist/docx/runParser.js +17 -1
  16. package/dist/docx/serializer/commentSerializer.js +12 -7
  17. package/dist/docx/serializer/runSerializer.js +16 -10
  18. package/dist/docx/serializer/sectionPropertiesSerializer.js +3 -2
  19. package/dist/docx/shapeParser.js +5 -113
  20. package/dist/docx/tableParser.js +8 -6
  21. package/dist/docx/textBoxParser.js +1 -1
  22. package/dist/model.d.ts +3 -3
  23. package/dist/model.js +2 -2
  24. package/dist/prosemirror/attrs/index.d.ts +4 -2
  25. package/dist/prosemirror/attrs/index.js +17 -2
  26. package/dist/prosemirror/conversion/fromProseDoc.js +10 -6
  27. package/dist/prosemirror/conversion/toProseDoc.js +1 -1
  28. package/dist/prosemirror/extensions/nodes/TabExtension.js +32 -9
  29. package/dist/prosemirror/schema/index.d.ts +2 -2
  30. package/dist/prosemirror/schema/nodes.d.ts +4 -1
  31. package/dist/prosemirror/validation.js +5 -3
  32. package/dist/types/content.d.ts +2 -2
  33. package/dist/types/documentEnumValues.d.ts +4 -1
  34. package/dist/types/documentEnumValues.js +14 -1
  35. package/dist/types/index.d.ts +2 -1
  36. package/package.json +2 -2
@@ -1,6 +1,26 @@
1
- import { findChild, getAttribute, getChildElements, parseXml } from "./xmlParser.js";
1
+ import { findChild, getAttribute, getChildElements, getLocalName, parseXml } from "./xmlParser.js";
2
+ import { parseRunProperties } from "./runParser.js";
2
3
  import { parseParagraph } from "./paragraphParser.js";
3
4
  //#region src/docx/commentParser.ts
5
+ const DEFAULT_ANNOTATION_REFERENCE_STYLE_ID = "CommentReference";
6
+ const normalizeAnnotationReferenceFormatting = (formatting) => {
7
+ if (formatting?.styleId === DEFAULT_ANNOTATION_REFERENCE_STYLE_ID && Object.keys(formatting).length === 1) return;
8
+ return formatting;
9
+ };
10
+ const normalizeFirstCommentParagraph = (paragraphElement, paragraph, theme) => {
11
+ const firstContentElement = getChildElements(paragraphElement).find((element) => getLocalName(element.name) !== "pPr");
12
+ if (!firstContentElement || getLocalName(firstContentElement.name) !== "r" || !findChild(firstContentElement, "w", "annotationRef")) return { paragraph };
13
+ const runProperties = findChild(firstContentElement, "w", "rPr");
14
+ const annotationReferenceFormatting = normalizeAnnotationReferenceFormatting(runProperties ? parseRunProperties(runProperties, theme) : void 0);
15
+ const firstParsedContent = paragraph.content.at(0);
16
+ return {
17
+ paragraph: firstParsedContent?.type === "run" && firstParsedContent.content.length === 0 ? {
18
+ ...paragraph,
19
+ content: paragraph.content.slice(1)
20
+ } : paragraph,
21
+ ...annotationReferenceFormatting !== void 0 ? { annotationReferenceFormatting } : {}
22
+ };
23
+ };
4
24
  /**
5
25
  * Build a lookup from paraId → dateUtc from commentsExtensible.xml
6
26
  *
@@ -91,9 +111,16 @@ function parseComments(commentsXml, styles, theme, rels, media, commentsExtensib
91
111
  const date = (paraId ? dateUtcByParaId.get(paraId) : void 0) ?? localDate;
92
112
  const done = (paraId ? extendedByParaId.get(paraId) : void 0)?.done;
93
113
  const paragraphs = [];
114
+ let annotationReferenceFormatting;
94
115
  for (const contentChild of getChildElements(child)) if ((contentChild.name?.replace(/^.*:/u, "") ?? "") === "p") {
95
116
  const paragraph = parseParagraph(contentChild, styles, theme, null, rels, media);
96
- paragraphs.push(paragraph);
117
+ if (paragraphs.length > 0) {
118
+ paragraphs.push(paragraph);
119
+ continue;
120
+ }
121
+ const normalized = normalizeFirstCommentParagraph(contentChild, paragraph, theme);
122
+ annotationReferenceFormatting = normalized.annotationReferenceFormatting;
123
+ paragraphs.push(normalized.paragraph);
97
124
  }
98
125
  const commentIndex = comments.length;
99
126
  if (paraId) {
@@ -106,6 +133,7 @@ function parseComments(commentsXml, styles, theme, rels, media, commentsExtensib
106
133
  ...initials !== void 0 ? { initials } : {},
107
134
  ...date !== void 0 ? { date } : {},
108
135
  ...done !== void 0 ? { done } : {},
136
+ ...annotationReferenceFormatting !== void 0 ? { annotationReferenceFormatting } : {},
109
137
  content: paragraphs
110
138
  });
111
139
  }
@@ -0,0 +1,5 @@
1
+ import { DocxParagraphSurfaces } from "./paragraphTraversal.js";
2
+ //#region src/docx/drawingIdNormalization.d.ts
3
+ declare const normalizeDrawingIds: (surfaces: DocxParagraphSurfaces) => void;
4
+ //#endregion
5
+ export { normalizeDrawingIds };
@@ -0,0 +1,35 @@
1
+ import { visitDocxParagraphs, visitParagraphRuns } from "./paragraphTraversal.js";
2
+ //#region src/docx/drawingIdNormalization.ts
3
+ const GENERATED_DRAWING_ID_START = 1e5;
4
+ const needsGeneratedId = ({ id }) => id === void 0 || id === "" || id === "0";
5
+ const normalizeDrawingIds = (surfaces) => {
6
+ const entries = [];
7
+ visitDocxParagraphs(surfaces, (paragraph) => {
8
+ visitParagraphRuns(paragraph, (run) => {
9
+ for (const content of run.content) {
10
+ if (content.type === "shape") {
11
+ entries.push({
12
+ drawing: content.shape,
13
+ generateWhenMissing: true
14
+ });
15
+ continue;
16
+ }
17
+ if (content.type === "drawing") entries.push({
18
+ drawing: content.image,
19
+ generateWhenMissing: content.rawXml === void 0
20
+ });
21
+ }
22
+ });
23
+ });
24
+ const usedIds = new Set(entries.flatMap(({ drawing }) => needsGeneratedId(drawing) ? [] : [drawing.id]));
25
+ let nextId = GENERATED_DRAWING_ID_START;
26
+ for (const { drawing, generateWhenMissing } of entries) {
27
+ if (!generateWhenMissing || !needsGeneratedId(drawing)) continue;
28
+ while (usedIds.has(String(nextId))) nextId += 1;
29
+ drawing.id = String(nextId);
30
+ usedIds.add(drawing.id);
31
+ nextId += 1;
32
+ }
33
+ };
34
+ //#endregion
35
+ export { normalizeDrawingIds };
@@ -48,7 +48,7 @@ declare function parsePositionV(posV: XmlElement | null): document_d_exports.Ima
48
48
  /**
49
49
  * Parse position for anchored drawings (combines positionH + positionV).
50
50
  */
51
- declare function parseAnchorPosition(anchor: XmlElement): document_d_exports.ImagePosition | undefined;
51
+ declare function parseAnchorPosition(anchor: XmlElement): document_d_exports.ImagePosition;
52
52
  /** Known wrap element names */
53
53
  declare const WRAP_ELEMENT_NAMES: string[];
54
54
  /**
@@ -1,5 +1,5 @@
1
1
  import { ImageHorizontalAlignmentSchema, ImageHorizontalRelativeToSchema, ImageVerticalAlignmentSchema, ImageVerticalRelativeToSchema, ImageWrapTextSchema, ShapeOutlineStyleSchema, narrowEnum } from "./parserEnums.js";
2
- import { findByFullName, getAttribute, getChildElements, getTextContent, parseNumericAttribute } from "./xmlParser.js";
2
+ import { elementToXml, findByFullName, findChildByLocalName, findChildrenByLocalName, getAttribute, getChildElements, getTextContent, parseNumericAttribute } from "./xmlParser.js";
3
3
  //#region src/docx/drawingUtils.ts
4
4
  /**
5
5
  * Map OOXML scheme names to standard theme color slots.
@@ -119,9 +119,8 @@ function parseColorElement(element) {
119
119
  */
120
120
  function parseFill(spPr) {
121
121
  if (!spPr) return;
122
- const children = getChildElements(spPr);
123
- if (children.some((el) => el.name === "a:noFill")) return { type: "none" };
124
- const solidFill = children.find((el) => el.name === "a:solidFill");
122
+ if (findChildByLocalName(spPr, "noFill")) return { type: "none" };
123
+ const solidFill = findChildByLocalName(spPr, "solidFill");
125
124
  if (solidFill) {
126
125
  const color = parseColorElement(solidFill);
127
126
  return color !== void 0 ? {
@@ -129,31 +128,105 @@ function parseFill(spPr) {
129
128
  color
130
129
  } : { type: "solid" };
131
130
  }
132
- if (children.some((el) => el.name === "a:gradFill")) return { type: "gradient" };
131
+ const gradientFill = findChildByLocalName(spPr, "gradFill");
132
+ if (gradientFill) return parseGradientFill(gradientFill);
133
+ }
134
+ function parseGradientFill(gradientFill) {
135
+ let type = "linear";
136
+ let angle;
137
+ const linear = findChildByLocalName(gradientFill, "lin");
138
+ if (linear) {
139
+ const authoredAngle = getAttribute(linear, null, "ang");
140
+ if (authoredAngle) {
141
+ const parsedAngle = Number.parseInt(authoredAngle, 10);
142
+ angle = Number.isNaN(parsedAngle) ? void 0 : parsedAngle / 6e4;
143
+ }
144
+ }
145
+ const path = findChildByLocalName(gradientFill, "path");
146
+ if (path) {
147
+ const pathType = getAttribute(path, null, "path");
148
+ if (pathType === "circle") type = "radial";
149
+ else if (pathType === "rect") type = "rectangular";
150
+ else type = "path";
151
+ }
152
+ const stops = [];
153
+ const stopList = findChildByLocalName(gradientFill, "gsLst");
154
+ if (stopList) for (const stop of findChildrenByLocalName(stopList, "gs")) {
155
+ const authoredPosition = getAttribute(stop, null, "pos");
156
+ const parsedPosition = authoredPosition ? Number.parseInt(authoredPosition, 10) : 0;
157
+ const color = parseColorElement(stop);
158
+ if (color) stops.push({
159
+ position: Number.isNaN(parsedPosition) ? 0 : parsedPosition,
160
+ color
161
+ });
162
+ }
163
+ return {
164
+ type: "gradient",
165
+ rawXml: elementToXml(gradientFill),
166
+ gradient: {
167
+ type,
168
+ ...angle !== void 0 ? { angle } : {},
169
+ stops
170
+ }
171
+ };
133
172
  }
134
173
  /**
135
174
  * Parse outline from shape properties (a:ln).
136
175
  */
137
176
  function parseOutline(spPr) {
138
- const ln = spPr ? findByFullName(spPr, "a:ln") : null;
177
+ const ln = spPr ? findChildByLocalName(spPr, "ln") : null;
139
178
  if (!ln) return;
140
- const children = getChildElements(ln);
141
- if (children.some((el) => el.name === "a:noFill")) return;
142
- const outline = {};
179
+ if (findChildByLocalName(ln, "noFill")) return;
180
+ const outline = { rawXml: elementToXml(ln) };
143
181
  const w = getAttribute(ln, null, "w");
144
- if (w) outline.width = Number.parseInt(w, 10);
145
- const solidFill = children.find((el) => el.name === "a:solidFill");
182
+ if (w) {
183
+ const parsed = Number.parseInt(w, 10);
184
+ if (!Number.isNaN(parsed)) outline.width = parsed;
185
+ }
186
+ const cap = getAttribute(ln, null, "cap");
187
+ if (cap === "flat") outline.cap = "flat";
188
+ else if (cap === "rnd") outline.cap = "round";
189
+ else if (cap === "sq") outline.cap = "square";
190
+ if (findChildByLocalName(ln, "bevel")) outline.join = "bevel";
191
+ else if (findChildByLocalName(ln, "round")) outline.join = "round";
192
+ else if (findChildByLocalName(ln, "miter")) outline.join = "miter";
193
+ const solidFill = findChildByLocalName(ln, "solidFill");
146
194
  if (solidFill) {
147
195
  const color = parseColorElement(solidFill);
148
196
  if (color !== void 0) outline.color = color;
149
197
  }
150
- const prstDash = children.find((el) => el.name === "a:prstDash");
198
+ const prstDash = findChildByLocalName(ln, "prstDash");
151
199
  if (prstDash) {
152
200
  const val = narrowEnum(getAttribute(prstDash, null, "val"), ShapeOutlineStyleSchema);
153
201
  if (val) outline.style = val;
154
202
  }
203
+ const headEnd = findChildByLocalName(ln, "headEnd");
204
+ if (headEnd) outline.headEnd = parseLineEnd(headEnd);
205
+ const tailEnd = findChildByLocalName(ln, "tailEnd");
206
+ if (tailEnd) outline.tailEnd = parseLineEnd(tailEnd);
155
207
  return outline;
156
208
  }
209
+ const LINE_END_TYPES = [
210
+ "none",
211
+ "triangle",
212
+ "stealth",
213
+ "diamond",
214
+ "oval",
215
+ "arrow"
216
+ ];
217
+ function parseLineEnd(element) {
218
+ const authoredType = getAttribute(element, null, "type");
219
+ const type = LINE_END_TYPES.find((allowed) => allowed === authoredType) ?? "none";
220
+ const authoredWidth = getAttribute(element, null, "w");
221
+ const authoredLength = getAttribute(element, null, "len");
222
+ const width = authoredWidth === "sm" || authoredWidth === "med" || authoredWidth === "lg" ? authoredWidth : void 0;
223
+ const length = authoredLength === "sm" || authoredLength === "med" || authoredLength === "lg" ? authoredLength : void 0;
224
+ return {
225
+ type,
226
+ ...width !== void 0 ? { width } : {},
227
+ ...length !== void 0 ? { length } : {}
228
+ };
229
+ }
157
230
  /**
158
231
  * Parse horizontal position from wp:positionH element.
159
232
  */
@@ -162,11 +235,11 @@ function parsePositionH(posH) {
162
235
  const relativeTo = narrowEnum(getAttribute(posH, null, "relativeFrom"), ImageHorizontalRelativeToSchema) ?? "column";
163
236
  const alignEl = findByFullName(posH, "wp:align");
164
237
  if (alignEl) {
165
- const text = getTextContent(alignEl);
166
- const result = { relativeTo };
167
- const alignment = narrowEnum(text, ImageHorizontalAlignmentSchema);
168
- if (alignment) result.alignment = alignment;
169
- return result;
238
+ const alignment = narrowEnum(getTextContent(alignEl), ImageHorizontalAlignmentSchema);
239
+ if (alignment) return {
240
+ relativeTo,
241
+ alignment
242
+ };
170
243
  }
171
244
  const posOffsetEl = findByFullName(posH, "wp:posOffset");
172
245
  if (posOffsetEl) {
@@ -177,7 +250,10 @@ function parsePositionH(posH) {
177
250
  posOffset: Number.isNaN(posOffset) ? 0 : posOffset
178
251
  };
179
252
  }
180
- return { relativeTo };
253
+ return {
254
+ relativeTo,
255
+ posOffset: 0
256
+ };
181
257
  }
182
258
  /**
183
259
  * Parse vertical position from wp:positionV element.
@@ -187,11 +263,11 @@ function parsePositionV(posV) {
187
263
  const relativeTo = narrowEnum(getAttribute(posV, null, "relativeFrom"), ImageVerticalRelativeToSchema) ?? "paragraph";
188
264
  const alignEl = findByFullName(posV, "wp:align");
189
265
  if (alignEl) {
190
- const text = getTextContent(alignEl);
191
- const result = { relativeTo };
192
- const alignment = narrowEnum(text, ImageVerticalAlignmentSchema);
193
- if (alignment) result.alignment = alignment;
194
- return result;
266
+ const alignment = narrowEnum(getTextContent(alignEl), ImageVerticalAlignmentSchema);
267
+ if (alignment) return {
268
+ relativeTo,
269
+ alignment
270
+ };
195
271
  }
196
272
  const posOffsetEl = findByFullName(posV, "wp:posOffset");
197
273
  if (posOffsetEl) {
@@ -202,7 +278,10 @@ function parsePositionV(posV) {
202
278
  posOffset: Number.isNaN(posOffset) ? 0 : posOffset
203
279
  };
204
280
  }
205
- return { relativeTo };
281
+ return {
282
+ relativeTo,
283
+ posOffset: 0
284
+ };
206
285
  }
207
286
  /**
208
287
  * Parse position for anchored drawings (combines positionH + positionV).
@@ -210,10 +289,15 @@ function parsePositionV(posV) {
210
289
  function parseAnchorPosition(anchor) {
211
290
  const positionH = findByFullName(anchor, "wp:positionH");
212
291
  const positionV = findByFullName(anchor, "wp:positionV");
213
- if (!positionH && !positionV) return;
214
292
  return {
215
- horizontal: parsePositionH(positionH) ?? { relativeTo: "column" },
216
- vertical: parsePositionV(positionV) ?? { relativeTo: "paragraph" }
293
+ horizontal: parsePositionH(positionH) ?? {
294
+ relativeTo: "column",
295
+ posOffset: 0
296
+ },
297
+ vertical: parsePositionV(positionV) ?? {
298
+ relativeTo: "paragraph",
299
+ posOffset: 0
300
+ }
217
301
  };
218
302
  }
219
303
  /** Known wrap element names */
@@ -14,6 +14,7 @@ type HeaderFooterWithVerbatim = document_d_exports.HeaderFooter & {
14
14
  declare const getHeaderFooterVerbatimXml: (hf: document_d_exports.HeaderFooter) => string | undefined;
15
15
  declare const canReplayHeaderFooterVerbatim: (hf: document_d_exports.HeaderFooter) => boolean;
16
16
  declare const assignHeaderFooterVerbatimXml: (hf: document_d_exports.HeaderFooter, xml: string) => void;
17
+ declare const refreshHeaderFooterVerbatimFingerprint: (hf: document_d_exports.HeaderFooter) => void;
17
18
  declare const clearHeaderFooterVerbatimXml: (hf: document_d_exports.HeaderFooter) => void;
18
19
  //#endregion
19
- export { HeaderFooterWithVerbatim, assignHeaderFooterVerbatimXml, canReplayHeaderFooterVerbatim, clearHeaderFooterVerbatimXml, getHeaderFooterVerbatimXml };
20
+ export { HeaderFooterWithVerbatim, assignHeaderFooterVerbatimXml, canReplayHeaderFooterVerbatim, clearHeaderFooterVerbatimXml, getHeaderFooterVerbatimXml, refreshHeaderFooterVerbatimFingerprint };
@@ -16,10 +16,15 @@ const assignHeaderFooterVerbatimXml = (hf, xml) => {
16
16
  ext.verbatimXml = xml;
17
17
  ext.verbatimFingerprint = headerFooterSerializationFingerprint(hf);
18
18
  };
19
+ const refreshHeaderFooterVerbatimFingerprint = (hf) => {
20
+ const ext = hf;
21
+ if (!ext.verbatimXml) return;
22
+ ext.verbatimFingerprint = headerFooterSerializationFingerprint(hf);
23
+ };
19
24
  const clearHeaderFooterVerbatimXml = (hf) => {
20
25
  const ext = hf;
21
26
  delete ext.verbatimXml;
22
27
  delete ext.verbatimFingerprint;
23
28
  };
24
29
  //#endregion
25
- export { assignHeaderFooterVerbatimXml, canReplayHeaderFooterVerbatim, clearHeaderFooterVerbatimXml, getHeaderFooterVerbatimXml };
30
+ export { assignHeaderFooterVerbatimXml, canReplayHeaderFooterVerbatim, clearHeaderFooterVerbatimXml, getHeaderFooterVerbatimXml, refreshHeaderFooterVerbatimFingerprint };
@@ -1,5 +1,5 @@
1
1
  import { BorderStyleSchema, FrameWrapSchema, FrameXAlignSchema, FrameYAlignSchema, LineSpacingRuleSchema, ParagraphAlignmentSchema, ShadingPatternSchema, TabLeaderSchema, TabStopAlignmentSchema, ThemeColorSlotSchema, narrowEnum } from "./parserEnums.js";
2
- import { elementToXml, findChild, findChildren, getAttribute, getChildElements, getLocalName, mergeXmlnsDeclarations, parseBooleanElement, parseNumberingLevelAttribute, parseNumericAttribute } from "./xmlParser.js";
2
+ import { elementToXml, findChild, findChildren, getAttribute, getChildElements, getLocalName, matchesName, mergeXmlnsDeclarations, parseBooleanElement, parseNumberingLevelAttribute, parseNumericAttribute } from "./xmlParser.js";
3
3
  import { parseRun, parseRunProperties } from "./runParser.js";
4
4
  import { parseFieldType } from "./fieldParser.js";
5
5
  import { isValidHexId } from "../utils/hexId.js";
@@ -684,6 +684,9 @@ function parseSimpleField(node, styles, theme, rels, media, rootXmlns = {}) {
684
684
  }
685
685
  return field;
686
686
  }
687
+ function hasRunPayloadElement(runElement) {
688
+ return getChildElements(runElement).some((child) => !matchesName(child, "w", "rPr"));
689
+ }
687
690
  /**
688
691
  * Parse all content within a paragraph
689
692
  *
@@ -789,7 +792,7 @@ function parseParagraphContents(paraElement, styles, theme, _numbering, rels, me
789
792
  type: "commentReference",
790
793
  id: commentReferenceId
791
794
  });
792
- } else contents.push(run);
795
+ } else if (run.content.length > 0 || hasRunPayloadElement(runElement)) contents.push(run);
793
796
  break;
794
797
  }
795
798
  case "hyperlink":
@@ -1,12 +1,14 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  //#region src/docx/paragraphTraversal.d.ts
3
- type VisitDocxParagraphsInput = {
3
+ type DocxParagraphSurfaces = {
4
4
  documentBody: document_d_exports.DocumentBody;
5
5
  headers?: Map<string, document_d_exports.HeaderFooter> | undefined;
6
6
  footers?: Map<string, document_d_exports.HeaderFooter> | undefined;
7
7
  footnotes?: readonly document_d_exports.Footnote[] | undefined;
8
8
  endnotes?: readonly document_d_exports.Endnote[] | undefined;
9
9
  };
10
- declare const visitDocxParagraphs: ({ documentBody, headers, footers, footnotes, endnotes }: VisitDocxParagraphsInput, visit: (paragraph: document_d_exports.Paragraph) => void) => void;
10
+ /** Visit every run directly owned by a paragraph's inline-content tree. */
11
+ declare const visitParagraphRuns: (paragraph: document_d_exports.Paragraph, visit: (run: document_d_exports.Run) => void) => void;
12
+ declare const visitDocxParagraphs: ({ documentBody, headers, footers, footnotes, endnotes }: DocxParagraphSurfaces, visit: (paragraph: document_d_exports.Paragraph) => void) => void;
11
13
  //#endregion
12
- export { visitDocxParagraphs };
14
+ export { DocxParagraphSurfaces, visitDocxParagraphs, visitParagraphRuns };
@@ -1,17 +1,8 @@
1
1
  //#region src/docx/paragraphTraversal.ts
2
- const visitDocxParagraphs = ({ documentBody, headers, footers, footnotes, endnotes }, visit) => {
3
- const seenParagraphs = /* @__PURE__ */ new WeakSet();
4
- const visitParagraph = (paragraph) => {
5
- if (seenParagraphs.has(paragraph)) return;
6
- seenParagraphs.add(paragraph);
7
- visit(paragraph);
8
- for (const content of paragraph.content) visitParagraphContent(content);
9
- };
2
+ /** Visit every run directly owned by a paragraph's inline-content tree. */
3
+ const visitParagraphRuns = (paragraph, visit) => {
10
4
  const visitRun = (run) => {
11
- for (const content of run.content) {
12
- if (content.type !== "shape" || !content.shape.textBody) continue;
13
- for (const block of content.shape.textBody.content) visitBlock(block);
14
- }
5
+ visit(run);
15
6
  };
16
7
  const visitHyperlink = (hyperlink) => {
17
8
  for (const child of hyperlink.children) if (child.type === "run") visitRun(child);
@@ -47,6 +38,22 @@ const visitDocxParagraphs = ({ documentBody, headers, footers, footnotes, endnot
47
38
  for (const run of content.fieldResult) visitRun(run);
48
39
  }
49
40
  };
41
+ for (const content of paragraph.content) visitParagraphContent(content);
42
+ };
43
+ const visitDocxParagraphs = ({ documentBody, headers, footers, footnotes, endnotes }, visit) => {
44
+ const seenParagraphs = /* @__PURE__ */ new WeakSet();
45
+ const visitParagraph = (paragraph) => {
46
+ if (seenParagraphs.has(paragraph)) return;
47
+ seenParagraphs.add(paragraph);
48
+ visit(paragraph);
49
+ visitParagraphRuns(paragraph, visitRun);
50
+ };
51
+ const visitRun = (run) => {
52
+ for (const content of run.content) {
53
+ if (content.type !== "shape" || !content.shape.textBody) continue;
54
+ for (const block of content.shape.textBody.content) visitBlock(block);
55
+ }
56
+ };
50
57
  const visitTable = (table) => {
51
58
  for (const row of table.rows) for (const cell of row.cells) visitBlocks(cell.content);
52
59
  };
@@ -73,4 +80,4 @@ const visitDocxParagraphs = ({ documentBody, headers, footers, footnotes, endnot
73
80
  for (const comment of documentBody.comments ?? []) for (const paragraph of comment.content) visitParagraph(paragraph);
74
81
  };
75
82
  //#endregion
76
- export { visitDocxParagraphs };
83
+ export { visitDocxParagraphs, visitParagraphRuns };
@@ -9,8 +9,9 @@ import { normalizeCommentReferences } from "./commentReferenceNormalization.js";
9
9
  import { detectDocxConformanceClass } from "./conformance.js";
10
10
  import { parseCoreProperties } from "./corePropertiesParser.js";
11
11
  import { extractAllTemplateVariables, parseDocumentBody } from "./documentParser.js";
12
+ import { normalizeDrawingIds } from "./drawingIdNormalization.js";
12
13
  import { parseEndnotes, parseFootnotes } from "./footnoteParser.js";
13
- import { assignHeaderFooterVerbatimXml } from "./headerFooterVerbatim.js";
14
+ import { assignHeaderFooterVerbatimXml, refreshHeaderFooterVerbatimFingerprint } from "./headerFooterVerbatim.js";
14
15
  import { parseFooter, parseHeader } from "./headerFooterParser.js";
15
16
  import { normalizeHeaderFooterReferences } from "./headerFooterReferenceNormalization.js";
16
17
  import { DocxModelValidationError, formatDocumentModelIssues, validateFolioDocumentModel } from "./modelValidation.js";
@@ -123,6 +124,15 @@ async function parseDocx(input, options = {}) {
123
124
  onProgress("Parsing comments...", 75);
124
125
  const comments = timeStage("comments", () => parseComments(raw.commentsXml, styles, theme, rels, media, raw.commentsExtensibleXml, raw.commentsExtendedXml));
125
126
  if (comments.length > 0) documentBody.comments = comments;
127
+ normalizeDrawingIds({
128
+ documentBody,
129
+ ...headers !== void 0 ? { headers } : {},
130
+ ...footers !== void 0 ? { footers } : {},
131
+ ...footnotes !== void 0 ? { footnotes } : {},
132
+ ...endnotes !== void 0 ? { endnotes } : {}
133
+ });
134
+ for (const header of headers?.values() ?? []) refreshHeaderFooterVerbatimFingerprint(header);
135
+ for (const footer of footers?.values() ?? []) refreshHeaderFooterVerbatimFingerprint(footer);
126
136
  normalizeRenderedPageBreakHints({
127
137
  documentBody,
128
138
  ...headers !== void 0 ? { headers } : {},
@@ -19,6 +19,9 @@ declare const ParagraphAlignmentSchema: v.PicklistSchema<readonly ["left", "cent
19
19
  declare const LineSpacingRuleSchema: v.PicklistSchema<readonly ["auto", "exact", "atLeast"], undefined>;
20
20
  declare const TabStopAlignmentSchema: v.PicklistSchema<readonly ["left", "center", "right", "decimal", "bar", "clear", "num"], undefined>;
21
21
  declare const TabLeaderSchema: v.PicklistSchema<readonly ["none", "dot", "hyphen", "underscore", "heavy", "middleDot"], undefined>;
22
+ declare const PositionalTabRelativeToSchema: v.PicklistSchema<readonly ["margin", "indent"], undefined>;
23
+ declare const PositionalTabAlignmentSchema: v.PicklistSchema<readonly ["left", "center", "right"], undefined>;
24
+ declare const PositionalTabLeaderSchema: v.PicklistSchema<readonly ["none", "dot", "hyphen", "underscore", "middleDot"], undefined>;
22
25
  declare const FrameXAlignSchema: v.PicklistSchema<readonly ["left", "center", "right", "inside", "outside"], undefined>;
23
26
  declare const FrameYAlignSchema: v.PicklistSchema<readonly ["top", "center", "bottom", "inside", "outside", "inline"], undefined>;
24
27
  declare const FrameWrapSchema: v.PicklistSchema<readonly ["around", "auto", "none", "notBeside", "through", "tight"], undefined>;
@@ -42,4 +45,4 @@ declare const ShapeTypeSchema: v.PicklistSchema<readonly ["rect", "roundRect", "
42
45
  declare const NumberFormatSchema: v.PicklistSchema<readonly ["decimal", "upperRoman", "lowerRoman", "upperLetter", "lowerLetter", "ordinal", "cardinalText", "ordinalText", "hex", "chicago", "ideographDigital", "japaneseCounting", "aiueo", "iroha", "decimalFullWidth", "decimalHalfWidth", "japaneseLegal", "japaneseDigitalTenThousand", "decimalEnclosedCircle", "decimalFullWidth2", "aiueoFullWidth", "irohaFullWidth", "decimalZero", "decimalZero3", "decimalZero4", "decimalZero5", "bullet", "ganada", "chosung", "decimalEnclosedFullstop", "decimalEnclosedParen", "decimalEnclosedCircleChinese", "ideographEnclosedCircle", "ideographTraditional", "ideographZodiac", "ideographZodiacTraditional", "taiwaneseCounting", "ideographLegalTraditional", "taiwaneseCountingThousand", "taiwaneseDigital", "chineseCounting", "chineseLegalSimplified", "chineseCountingThousand", "koreanDigital", "koreanCounting", "koreanLegal", "koreanDigital2", "vietnameseCounting", "russianLower", "russianUpper", "none", "numberInDash", "hebrew1", "hebrew2", "arabicAlpha", "arabicAbjad", "hindiVowels", "hindiConsonants", "hindiNumbers", "hindiCounting", "thaiLetters", "thaiNumbers", "thaiCounting"], undefined>;
43
46
  declare const LevelSuffixSchema: v.PicklistSchema<readonly ["tab", "space", "nothing"], undefined>;
44
47
  //#endregion
45
- export { BorderStyleSchema, ConditionalStyleTypeSchema, EmphasisMarkSchema, FieldTypeSchema, FloatingTableXSpecSchema, FloatingTableYSpecSchema, FontHintSchema, FontThemeSchema, FrameWrapSchema, FrameXAlignSchema, FrameYAlignSchema, HighlightColorSchema, ImageHorizontalAlignmentSchema, ImageHorizontalRelativeToSchema, ImageVerticalAlignmentSchema, ImageVerticalRelativeToSchema, ImageWrapTextSchema, LevelSuffixSchema, LineSpacingRuleSchema, NumberFormatSchema, ParagraphAlignmentSchema, SdtLockSchema, ShadingPatternSchema, ShapeOutlineStyleSchema, ShapeTypeSchema, StyleTypeSchema, TabLeaderSchema, TabStopAlignmentSchema, TableCellTextDirectionSchema, TableRowHeightRuleSchema, TableWidthTypeSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum };
48
+ export { BorderStyleSchema, ConditionalStyleTypeSchema, EmphasisMarkSchema, FieldTypeSchema, FloatingTableXSpecSchema, FloatingTableYSpecSchema, FontHintSchema, FontThemeSchema, FrameWrapSchema, FrameXAlignSchema, FrameYAlignSchema, HighlightColorSchema, ImageHorizontalAlignmentSchema, ImageHorizontalRelativeToSchema, ImageVerticalAlignmentSchema, ImageVerticalRelativeToSchema, ImageWrapTextSchema, LevelSuffixSchema, LineSpacingRuleSchema, NumberFormatSchema, ParagraphAlignmentSchema, PositionalTabAlignmentSchema, PositionalTabLeaderSchema, PositionalTabRelativeToSchema, SdtLockSchema, ShadingPatternSchema, ShapeOutlineStyleSchema, ShapeTypeSchema, StyleTypeSchema, TabLeaderSchema, TabStopAlignmentSchema, TableCellTextDirectionSchema, TableRowHeightRuleSchema, TableWidthTypeSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum };
@@ -1,4 +1,4 @@
1
- import { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, NUMBER_FORMAT_VALUES, PARAGRAPH_ALIGNMENT_VALUES, SDT_LOCK_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES } from "../types/documentEnumValues.js";
1
+ import { BORDER_STYLE_VALUES, CONDITIONAL_STYLE_TYPE_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FLOATING_TABLE_X_SPEC_VALUES, FLOATING_TABLE_Y_SPEC_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, FRAME_WRAP_VALUES, FRAME_X_ALIGN_VALUES, FRAME_Y_ALIGN_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, LEVEL_SUFFIX_VALUES, LINE_SPACING_RULE_VALUES, NUMBER_FORMAT_VALUES, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SHADING_PATTERN_VALUES, SHAPE_OUTLINE_STYLE_VALUES, SHAPE_TYPE_VALUES, STYLE_TYPE_VALUES, TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_EFFECT_VALUES, THEME_COLOR_SLOT_VALUES, UNDERLINE_STYLE_VALUES } from "../types/documentEnumValues.js";
2
2
  import * as v from "valibot";
3
3
  //#region src/docx/parserEnums.ts
4
4
  /**
@@ -45,6 +45,9 @@ const ParagraphAlignmentSchema = v.picklist(PARAGRAPH_ALIGNMENT_VALUES);
45
45
  const LineSpacingRuleSchema = v.picklist(LINE_SPACING_RULE_VALUES);
46
46
  const TabStopAlignmentSchema = v.picklist(TAB_STOP_ALIGNMENT_VALUES);
47
47
  const TabLeaderSchema = v.picklist(TAB_LEADER_VALUES);
48
+ const PositionalTabRelativeToSchema = v.picklist(POSITIONAL_TAB_RELATIVE_TO_VALUES);
49
+ const PositionalTabAlignmentSchema = v.picklist(POSITIONAL_TAB_ALIGNMENT_VALUES);
50
+ const PositionalTabLeaderSchema = v.picklist(POSITIONAL_TAB_LEADER_VALUES);
48
51
  const FrameXAlignSchema = v.picklist(FRAME_X_ALIGN_VALUES);
49
52
  const FrameYAlignSchema = v.picklist(FRAME_Y_ALIGN_VALUES);
50
53
  const FrameWrapSchema = v.picklist(FRAME_WRAP_VALUES);
@@ -68,4 +71,4 @@ const ShapeTypeSchema = v.picklist(SHAPE_TYPE_VALUES);
68
71
  const NumberFormatSchema = v.picklist(NUMBER_FORMAT_VALUES);
69
72
  const LevelSuffixSchema = v.picklist(LEVEL_SUFFIX_VALUES);
70
73
  //#endregion
71
- export { BorderStyleSchema, ConditionalStyleTypeSchema, EmphasisMarkSchema, FieldTypeSchema, FloatingTableXSpecSchema, FloatingTableYSpecSchema, FontHintSchema, FontThemeSchema, FrameWrapSchema, FrameXAlignSchema, FrameYAlignSchema, HighlightColorSchema, ImageHorizontalAlignmentSchema, ImageHorizontalRelativeToSchema, ImageVerticalAlignmentSchema, ImageVerticalRelativeToSchema, ImageWrapTextSchema, LevelSuffixSchema, LineSpacingRuleSchema, NumberFormatSchema, ParagraphAlignmentSchema, SdtLockSchema, ShadingPatternSchema, ShapeOutlineStyleSchema, ShapeTypeSchema, StyleTypeSchema, TabLeaderSchema, TabStopAlignmentSchema, TableCellTextDirectionSchema, TableRowHeightRuleSchema, TableWidthTypeSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum };
74
+ export { BorderStyleSchema, ConditionalStyleTypeSchema, EmphasisMarkSchema, FieldTypeSchema, FloatingTableXSpecSchema, FloatingTableYSpecSchema, FontHintSchema, FontThemeSchema, FrameWrapSchema, FrameXAlignSchema, FrameYAlignSchema, HighlightColorSchema, ImageHorizontalAlignmentSchema, ImageHorizontalRelativeToSchema, ImageVerticalAlignmentSchema, ImageVerticalRelativeToSchema, ImageWrapTextSchema, LevelSuffixSchema, LineSpacingRuleSchema, NumberFormatSchema, ParagraphAlignmentSchema, PositionalTabAlignmentSchema, PositionalTabLeaderSchema, PositionalTabRelativeToSchema, SdtLockSchema, ShadingPatternSchema, ShapeOutlineStyleSchema, ShapeTypeSchema, StyleTypeSchema, TabLeaderSchema, TabStopAlignmentSchema, TableCellTextDirectionSchema, TableRowHeightRuleSchema, TableWidthTypeSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum };
@@ -161,7 +161,6 @@ function consolidateRuns(runs) {
161
161
  return result;
162
162
  }
163
163
  function consolidateParagraphContent(content) {
164
- if (content.length <= 1) return content;
165
164
  const result = [];
166
165
  const pendingRuns = [];
167
166
  function flushRuns() {
@@ -1,4 +1,4 @@
1
- import { EmphasisMarkSchema, FontHintSchema, FontThemeSchema, HighlightColorSchema, ShadingPatternSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum } from "./parserEnums.js";
1
+ import { EmphasisMarkSchema, FontHintSchema, FontThemeSchema, HighlightColorSchema, PositionalTabAlignmentSchema, PositionalTabLeaderSchema, PositionalTabRelativeToSchema, ShadingPatternSchema, TextEffectSchema, ThemeColorSlotSchema, UnderlineStyleSchema, narrowEnum } from "./parserEnums.js";
2
2
  import { cloneWithXmlnsDeclarations, elementToXml, findAllDeep, findChild, findChildren, getAttribute, getChildElements, getLocalName, getTextContent, mergeXmlnsDeclarations, parseBooleanElement, parseNumericAttribute } from "./xmlParser.js";
3
3
  import { isTextBoxDrawing } from "./textBoxParser.js";
4
4
  import { parseImage } from "./imageParser.js";
@@ -386,6 +386,19 @@ function parseTextContent(element) {
386
386
  function parseTabContent() {
387
387
  return { type: "tab" };
388
388
  }
389
+ function parsePositionalTabContent(element) {
390
+ const positional = {};
391
+ const relativeTo = narrowEnum(getAttribute(element, "w", "relativeTo"), PositionalTabRelativeToSchema);
392
+ const alignment = narrowEnum(getAttribute(element, "w", "alignment"), PositionalTabAlignmentSchema);
393
+ const leader = narrowEnum(getAttribute(element, "w", "leader"), PositionalTabLeaderSchema);
394
+ if (relativeTo !== void 0) positional.relativeTo = relativeTo;
395
+ if (alignment !== void 0) positional.alignment = alignment;
396
+ if (leader !== void 0) positional.leader = leader;
397
+ return {
398
+ type: "tab",
399
+ positional
400
+ };
401
+ }
389
402
  /**
390
403
  * Parse break element (w:br)
391
404
  */
@@ -516,6 +529,9 @@ function parseRunContents(runElement, rels, media, rootXmlns = {}) {
516
529
  case "tab":
517
530
  contents.push(parseTabContent());
518
531
  break;
532
+ case "ptab":
533
+ contents.push(parsePositionalTabContent(child));
534
+ break;
519
535
  case "br":
520
536
  contents.push(parseBreakContent(child));
521
537
  break;
@@ -1,5 +1,6 @@
1
1
  import { deterministicHexId } from "../../utils/hexId.js";
2
2
  import { escapeXml } from "./xmlUtils.js";
3
+ import { serializeTextFormatting } from "./runSerializer.js";
3
4
  import { serializeParagraph } from "./paragraphSerializer.js";
4
5
  //#region src/docx/serializer/commentSerializer.ts
5
6
  /**
@@ -7,27 +8,31 @@ import { serializeParagraph } from "./paragraphSerializer.js";
7
8
  *
8
9
  * Serializes Comment[] to OOXML comments.xml format.
9
10
  */
10
- const ANNOTATION_REFERENCE_XML = "<w:r><w:rPr><w:rStyle w:val=\"CommentReference\"/></w:rPr><w:annotationRef/></w:r>";
11
+ const DEFAULT_ANNOTATION_REFERENCE_PROPERTIES = "<w:rPr><w:rStyle w:val=\"CommentReference\"/></w:rPr>";
11
12
  const PARAGRAPH_PROPERTIES_END = "</w:pPr>";
13
+ const serializeAnnotationReference = (formatting) => {
14
+ return `<w:r>${formatting ? serializeTextFormatting(formatting) : DEFAULT_ANNOTATION_REFERENCE_PROPERTIES}<w:annotationRef/></w:r>`;
15
+ };
12
16
  /** Serialize a paragraph, prepending an annotationRef run (required by Word in first paragraph of a comment) */
13
- function serializeParagraphWithAnnotationRef(paragraph) {
17
+ function serializeParagraphWithAnnotationRef(paragraph, formatting) {
14
18
  const xml = serializeParagraph(paragraph);
19
+ const annotationReference = serializeAnnotationReference(formatting);
15
20
  const propertiesEnd = xml.indexOf(PARAGRAPH_PROPERTIES_END);
16
21
  if (propertiesEnd !== -1) {
17
22
  const contentStart = propertiesEnd + 8;
18
- return `${xml.slice(0, contentStart)}${ANNOTATION_REFERENCE_XML}${xml.slice(contentStart)}`;
23
+ return `${xml.slice(0, contentStart)}${annotationReference}${xml.slice(contentStart)}`;
19
24
  }
20
- return xml.replace(/^<w:p(?=[\s>])[^>]*>/u, (openingTag) => `${openingTag}${ANNOTATION_REFERENCE_XML}`);
25
+ return xml.replace(/^<w:p(?=[\s>])[^>]*>/u, (openingTag) => `${openingTag}${annotationReference}`);
21
26
  }
22
27
  function serializeComment(comment) {
23
28
  const attrs = [`w:id="${comment.id}"`, `w:author="${escapeXml(comment.author)}"`];
24
- if (comment.initials) attrs.push(`w:initials="${escapeXml(comment.initials)}"`);
29
+ if (comment.initials !== void 0) attrs.push(`w:initials="${escapeXml(comment.initials)}"`);
25
30
  if (comment.date) attrs.push(`w:date="${escapeXml(comment.date)}"`);
26
31
  let xml = `<w:comment ${attrs.join(" ")}>`;
27
32
  if (comment.content.length > 0) {
28
- xml += serializeParagraphWithAnnotationRef(comment.content[0]);
33
+ xml += serializeParagraphWithAnnotationRef(comment.content[0], comment.annotationReferenceFormatting);
29
34
  for (let i = 1; i < comment.content.length; i++) xml += serializeParagraph(comment.content[i]);
30
- } else xml += "<w:p><w:r><w:rPr><w:rStyle w:val=\"CommentReference\"/></w:rPr><w:annotationRef/></w:r></w:p>";
35
+ } else xml += `<w:p>${serializeAnnotationReference(comment.annotationReferenceFormatting)}</w:p>`;
31
36
  xml += "</w:comment>";
32
37
  return xml;
33
38
  }