docxmlater 10.2.0 → 10.2.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.
- package/dist/core/DocumentGenerator.d.ts.map +1 -1
- package/dist/core/DocumentGenerator.js +9 -0
- package/dist/core/DocumentGenerator.js.map +1 -1
- package/dist/core/DocumentParser.d.ts.map +1 -1
- package/dist/core/DocumentParser.js +56 -12
- package/dist/core/DocumentParser.js.map +1 -1
- package/dist/elements/Field.d.ts.map +1 -1
- package/dist/elements/Field.js +2 -0
- package/dist/elements/Field.js.map +1 -1
- package/dist/utils/InMemoryRevisionAcceptor.d.ts.map +1 -1
- package/dist/utils/InMemoryRevisionAcceptor.js +15 -0
- package/dist/utils/InMemoryRevisionAcceptor.js.map +1 -1
- package/package.json +1 -1
- package/src/core/DocumentGenerator.ts +9 -0
- package/src/core/DocumentParser.ts +69 -17
- package/src/elements/Field.ts +2 -0
- package/src/utils/InMemoryRevisionAcceptor.ts +13 -0
|
@@ -1238,8 +1238,14 @@ export class DocumentParser {
|
|
|
1238
1238
|
if (nextClose === -1) break;
|
|
1239
1239
|
if (nextOpen !== -1 && nextOpen < nextClose) {
|
|
1240
1240
|
const charAfter = paraContent[nextOpen + openTag.length];
|
|
1241
|
-
if (
|
|
1242
|
-
|
|
1241
|
+
if (
|
|
1242
|
+
charAfter === '>' ||
|
|
1243
|
+
charAfter === ' ' ||
|
|
1244
|
+
charAfter === '/' ||
|
|
1245
|
+
charAfter === '\t' ||
|
|
1246
|
+
charAfter === '\n' ||
|
|
1247
|
+
charAfter === '\r'
|
|
1248
|
+
) {
|
|
1243
1249
|
depth++;
|
|
1244
1250
|
}
|
|
1245
1251
|
searchFrom = nextOpen + openTag.length;
|
|
@@ -1323,19 +1329,32 @@ export class DocumentParser {
|
|
|
1323
1329
|
? [hyperlinks]
|
|
1324
1330
|
: [];
|
|
1325
1331
|
if (child.index < hyperlinkArray.length) {
|
|
1326
|
-
const
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1332
|
+
const hyperlinkObj = hyperlinkArray[child.index];
|
|
1333
|
+
|
|
1334
|
+
// Hyperlinks containing tracked changes (w:del/w:ins inside w:hyperlink)
|
|
1335
|
+
// cannot survive parseHyperlinkFromObject round-trip — preserve as raw XML
|
|
1336
|
+
const hasRevisionChildren =
|
|
1337
|
+
hyperlinkObj['w:del'] ||
|
|
1338
|
+
hyperlinkObj['w:ins'] ||
|
|
1339
|
+
hyperlinkObj['w:moveFrom'] ||
|
|
1340
|
+
hyperlinkObj['w:moveTo'];
|
|
1341
|
+
if (hasRevisionChildren) {
|
|
1342
|
+
const rawXml = extractElementXmlAtPosition(child.pos, 'w:hyperlink');
|
|
1343
|
+
if (rawXml) {
|
|
1344
|
+
paragraph.addContent(new PreservedElement(rawXml, 'w:hyperlink', 'inline'));
|
|
1345
|
+
}
|
|
1346
|
+
} else {
|
|
1347
|
+
const result = this.parseHyperlinkFromObject(hyperlinkObj, relationshipManager);
|
|
1348
|
+
if (result.hyperlink) {
|
|
1349
|
+
paragraph.addHyperlink(result.hyperlink);
|
|
1350
|
+
}
|
|
1351
|
+
// Add any bookmarks found inside the hyperlink
|
|
1352
|
+
for (const bookmark of result.bookmarkStarts) {
|
|
1353
|
+
paragraph.addBookmarkStart(bookmark);
|
|
1354
|
+
}
|
|
1355
|
+
for (const bookmark of result.bookmarkEnds) {
|
|
1356
|
+
paragraph.addBookmarkEnd(bookmark);
|
|
1357
|
+
}
|
|
1339
1358
|
}
|
|
1340
1359
|
}
|
|
1341
1360
|
} else if (child.type === 'w:fldSimple') {
|
|
@@ -1347,8 +1366,12 @@ export class DocumentParser {
|
|
|
1347
1366
|
paragraph.addField(field);
|
|
1348
1367
|
}
|
|
1349
1368
|
}
|
|
1350
|
-
} else if (
|
|
1351
|
-
|
|
1369
|
+
} else if (
|
|
1370
|
+
child.type === 'w:ins' ||
|
|
1371
|
+
child.type === 'w:del' ||
|
|
1372
|
+
child.type === 'w:moveFrom' ||
|
|
1373
|
+
child.type === 'w:moveTo'
|
|
1374
|
+
) {
|
|
1352
1375
|
const revisionXml = extractElementXmlAtPosition(child.pos, child.type);
|
|
1353
1376
|
if (revisionXml) {
|
|
1354
1377
|
// Detect nested revision elements (e.g., w:del inside w:ins)
|
|
@@ -2811,6 +2834,35 @@ export class DocumentParser {
|
|
|
2811
2834
|
if (isHyperlinkInstruction(instruction)) {
|
|
2812
2835
|
const parsed = parseHyperlinkInstruction(instruction);
|
|
2813
2836
|
if (parsed && resultText) {
|
|
2837
|
+
// HYPERLINK fields with tracked changes in the result region cannot be
|
|
2838
|
+
// converted to Hyperlink objects — the revisions would become orphaned
|
|
2839
|
+
// standalone paragraph content. Keep as ComplexField instead.
|
|
2840
|
+
if (fieldRevisions.length > 0) {
|
|
2841
|
+
const complexField = this.createComplexFieldFromRuns(fieldRuns);
|
|
2842
|
+
if (complexField) {
|
|
2843
|
+
for (const rev of fieldRevisions) {
|
|
2844
|
+
rev.setFieldContext({
|
|
2845
|
+
field: complexField,
|
|
2846
|
+
instruction: complexField.getInstruction(),
|
|
2847
|
+
position: 'result',
|
|
2848
|
+
});
|
|
2849
|
+
}
|
|
2850
|
+
complexField.setResultRevisions(fieldRevisions);
|
|
2851
|
+
groupedContent.push(complexField);
|
|
2852
|
+
} else {
|
|
2853
|
+
fieldRuns.forEach((run) => groupedContent.push(run));
|
|
2854
|
+
for (const revision of fieldRevisions) {
|
|
2855
|
+
groupedContent.push(revision);
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
fieldRuns = [];
|
|
2859
|
+
instructionRevisions = [];
|
|
2860
|
+
fieldRevisions = [];
|
|
2861
|
+
fieldState = null;
|
|
2862
|
+
break;
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2865
|
+
// Normal path (no revisions) — convert to Hyperlink object
|
|
2814
2866
|
let hyperlink: Hyperlink;
|
|
2815
2867
|
if (parsed.url && parsed.url.trim() !== '') {
|
|
2816
2868
|
// External hyperlink (or combined external + anchor)
|
package/src/elements/Field.ts
CHANGED
|
@@ -685,6 +685,8 @@ export class ComplexField {
|
|
|
685
685
|
*/
|
|
686
686
|
setResult(result: string): this {
|
|
687
687
|
this.result = result;
|
|
688
|
+
this.resultRevisions = []; // Clear orphaned revisions from old text
|
|
689
|
+
this.resultContent = []; // Clear raw XML content since we have new text
|
|
688
690
|
return this;
|
|
689
691
|
}
|
|
690
692
|
|
|
@@ -22,6 +22,7 @@ import type { Run } from '../elements/Run';
|
|
|
22
22
|
import type { Hyperlink } from '../elements/Hyperlink';
|
|
23
23
|
import { isRunContent, isHyperlinkContent, isImageRunContent } from '../elements/RevisionContent';
|
|
24
24
|
import type { ImageRun } from '../elements/ImageRun';
|
|
25
|
+
import { ComplexField } from '../elements/Field';
|
|
25
26
|
import { Table } from '../elements/Table';
|
|
26
27
|
import { Section } from '../elements/Section';
|
|
27
28
|
import { getGlobalLogger, createScopedLogger, ILogger } from './logger';
|
|
@@ -544,6 +545,18 @@ function acceptRevisionsInParagraph(
|
|
|
544
545
|
// If we reach here, this revision type is not being accepted
|
|
545
546
|
// Keep it in the content
|
|
546
547
|
newContent.push(item);
|
|
548
|
+
} else if (item instanceof ComplexField && item.hasResultRevisions()) {
|
|
549
|
+
// Accept revisions nested inside ComplexField result sections
|
|
550
|
+
const fieldRevisions = item.getResultRevisions();
|
|
551
|
+
for (const rev of fieldRevisions) {
|
|
552
|
+
const revType = rev.getType();
|
|
553
|
+
if (revType === 'insert' && options.acceptInsertions) result.insertionsAccepted++;
|
|
554
|
+
else if (revType === 'delete' && options.acceptDeletions) result.deletionsAccepted++;
|
|
555
|
+
else if ((revType === 'moveFrom' || revType === 'moveTo') && options.acceptMoves)
|
|
556
|
+
result.movesAccepted++;
|
|
557
|
+
}
|
|
558
|
+
item.setResultRevisions([]); // Clear after acceptance
|
|
559
|
+
newContent.push(item);
|
|
547
560
|
} else {
|
|
548
561
|
// Non-revision content - keep as-is
|
|
549
562
|
newContent.push(item);
|