@docx4j/core-ts 0.3.0 → 0.3.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/model/content/Body.mjs +40 -19
- package/dist/model/content/Comment.d.mts +6 -0
- package/dist/model/content/Comment.mjs +76 -22
- package/dist/model/content/ContentControl.d.mts +11 -1
- package/dist/model/content/ContentControl.mjs +69 -10
- package/dist/model/content/Paragraph.d.mts +37 -2
- package/dist/model/content/Paragraph.mjs +140 -38
- package/dist/model/content/Range.mjs +49 -5
- package/dist/model/content/Table.mjs +6 -8
- package/dist/model/content/TrackedChange.d.mts +32 -4
- package/dist/model/content/TrackedChange.mjs +271 -34
- package/dist/model/content/index.d.mts +1 -1
- package/dist/model/content/index.mjs +1 -1
- package/dist/model/content/stylesFacade.mjs +9 -3
- package/dist/model/content/tracking.d.mts +65 -5
- package/dist/model/content/tracking.mjs +170 -43
- package/dist/model/customxml/XmlMapping.d.mts +20 -4
- package/dist/model/customxml/XmlMapping.mjs +44 -6
- package/dist/model/customxml/bindings.d.mts +9 -2
- package/dist/model/customxml/bindings.mjs +18 -2
- package/dist/model/customxml/index.d.mts +1 -1
- package/dist/model/customxml/index.mjs +1 -1
- package/dist/model/customxml/insert.d.mts +10 -0
- package/dist/model/customxml/insert.mjs +13 -0
- package/dist/model/fonts/RunFontSelector.d.mts +8 -1
- package/dist/model/fonts/RunFontSelector.mjs +6 -5
- package/dist/model/properties/PropertyResolver.d.mts +110 -10
- package/dist/model/properties/PropertyResolver.mjs +300 -31
- package/dist/model/properties/index.d.mts +3 -0
- package/dist/model/properties/index.mjs +2 -0
- package/dist/model/properties/table.d.mts +155 -0
- package/dist/model/properties/table.mjs +315 -0
- package/dist/model/properties/tableStyleConditions.d.mts +101 -0
- package/dist/model/properties/tableStyleConditions.mjs +296 -0
- package/dist/packages/WordprocessingMLPackage.d.mts +6 -0
- package/dist/packages/WordprocessingMLPackage.mjs +11 -0
- package/dist/parts/wml/commentStyles.d.mts +3 -0
- package/dist/parts/wml/commentStyles.mjs +25 -0
- package/dist/parts/wml/comments.mjs +4 -22
- package/dist/parts/wml/placeholderStyle.d.mts +3 -0
- package/dist/parts/wml/placeholderStyle.mjs +13 -0
- package/package.json +2 -2
|
@@ -6,13 +6,13 @@ import { Paragraph } from './Paragraph.mjs';
|
|
|
6
6
|
import { Table } from './Table.mjs';
|
|
7
7
|
import { ContentControl, collectControls } from './ContentControl.mjs';
|
|
8
8
|
import { sdt as sdtOf, nextSdtId } from '@docx4j/generated-objects-ts/builders/wml';
|
|
9
|
-
import { controlIdScope, sdtKindFor } from '../customxml/insert.mjs';
|
|
9
|
+
import { controlIdScope, sdtKindFor, declareSdtExtensions } from '../customxml/insert.mjs';
|
|
10
10
|
import { InlinePicture, addImage, writableWidthEmu } from './InlinePicture.mjs';
|
|
11
11
|
import { contentOf } from './ooxml.mjs';
|
|
12
12
|
import { expandReplacement } from './search.mjs';
|
|
13
13
|
import { commentApi } from './comments.mjs';
|
|
14
|
-
import { trackerOf,
|
|
15
|
-
import { trackedChangesOfRow, trackedChangeTokensOfParagraph, groupTouching, BREAK, tablePropertiesChangeOf, cellChangeOf, bodySectionChangeOf, resolveUnlisted } from './TrackedChange.mjs';
|
|
14
|
+
import { trackerOf, trackInsertedBlock, markHolderInserted } from './tracking.mjs';
|
|
15
|
+
import { trackedChangesOfRow, trackedChangeTokensOfParagraph, groupTouching, BREAK, tablePropertiesChangeOf, cellChangeOf, bodySectionChangeOf, resolveUnlisted, holderInsertionAt } from './TrackedChange.mjs';
|
|
16
16
|
import { listsOf, listLabelsOf } from './List.mjs';
|
|
17
17
|
/**
|
|
18
18
|
* A subset of Office JS `Word.Body` over any container with block-level content: the main
|
|
@@ -301,14 +301,12 @@ export class Body {
|
|
|
301
301
|
for (const el of elements) {
|
|
302
302
|
linkParents(el, owner);
|
|
303
303
|
const tn = typeNameOf(el);
|
|
304
|
-
if (tn === 'org_docx4j_wml.P')
|
|
304
|
+
if (tn === 'org_docx4j_wml.P')
|
|
305
305
|
this.assignParaId(el.value);
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
trackInsertedTable(tracker, el.value);
|
|
311
|
-
}
|
|
306
|
+
// everything it holds is an insertion: the runs in its links and inline controls, the rows and
|
|
307
|
+
// nested tables of a table, a block control's content and the control itself (CR-002 section 37)
|
|
308
|
+
if (tracker)
|
|
309
|
+
trackInsertedBlock(tracker, el, container);
|
|
312
310
|
}
|
|
313
311
|
const first = elements[0];
|
|
314
312
|
return typeNameOf(first) === 'org_docx4j_wml.P' ? new Paragraph(first, container, this) : { element: first, container };
|
|
@@ -387,22 +385,35 @@ export class Body {
|
|
|
387
385
|
getTrackedChanges() {
|
|
388
386
|
const out = [];
|
|
389
387
|
const visit = (items, into) => {
|
|
390
|
-
var _a;
|
|
391
|
-
for (
|
|
388
|
+
var _a, _b;
|
|
389
|
+
for (let i = 0; i < items.length; i++) {
|
|
390
|
+
const el = items[i];
|
|
392
391
|
const tn = typeNameOf(el);
|
|
393
392
|
if (tn === 'org_docx4j_wml.P') {
|
|
394
393
|
into.push(...trackedChangeTokensOfParagraph(new Paragraph(el, items, this)));
|
|
395
394
|
continue;
|
|
396
395
|
}
|
|
396
|
+
// a block control or custom XML element that is itself an insertion (check 30): one piece,
|
|
397
|
+
// grouping with the insertions around and inside it, as Word lists them
|
|
398
|
+
const inserted = holderInsertionAt(items, i, undefined);
|
|
399
|
+
if (inserted) {
|
|
400
|
+
into.push(inserted);
|
|
401
|
+
visit((_a = childrenOf(items[i + 1].value)) !== null && _a !== void 0 ? _a : [], into);
|
|
402
|
+
i += 2;
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
397
405
|
const v = el.value;
|
|
398
406
|
if (typeof v !== 'object' || v === null)
|
|
399
407
|
continue;
|
|
400
408
|
if (tn === 'org_docx4j_wml.Tbl') {
|
|
401
|
-
|
|
402
|
-
//
|
|
409
|
+
// A table is no break of its own: an inserted or deleted row groups with the rows and the
|
|
410
|
+
// inserted text touching it, as Office JS lists them (check 31: a three-row table between two
|
|
411
|
+
// inserted paragraphs is one change; rows added at a table's end with a paragraph typed after
|
|
412
|
+
// it, one; a paragraph whose mark is plain before rows added at the start, two). Its property
|
|
413
|
+
// records are one change of their own where the table stands (CR-002 section 35), kept apart.
|
|
403
414
|
const tableChange = tablePropertiesChangeOf(el);
|
|
404
415
|
if (tableChange)
|
|
405
|
-
into.push(tableChange, BREAK);
|
|
416
|
+
into.push(BREAK, tableChange, BREAK);
|
|
406
417
|
for (const row of rowsOf(v)) {
|
|
407
418
|
// A row that is itself a revision reports as one change, as Office JS does: the
|
|
408
419
|
// markup its cells carry (Word marks every run and mark of a deleted or inserted row)
|
|
@@ -412,13 +423,18 @@ export class Body {
|
|
|
412
423
|
const cellChange = cellChangeOf(cell.element, cell.container, row.element.value);
|
|
413
424
|
if (cellChange)
|
|
414
425
|
tokens.push(cellChange, BREAK);
|
|
415
|
-
visit((
|
|
426
|
+
visit((_b = childrenOf(cell.element.value)) !== null && _b !== void 0 ? _b : [], tokens);
|
|
416
427
|
tokens.push(BREAK);
|
|
417
428
|
}
|
|
418
429
|
const inner = groupTouching(tokens);
|
|
419
|
-
const rowChanges = trackedChangesOfRow(row.element, row.container, inner);
|
|
420
|
-
|
|
421
|
-
into.push(
|
|
430
|
+
const rowChanges = trackedChangesOfRow(row.element, row.container, inner, { element: el, container: items });
|
|
431
|
+
if (rowChanges.length > 0)
|
|
432
|
+
into.push(...rowChanges);
|
|
433
|
+
else {
|
|
434
|
+
into.push(BREAK);
|
|
435
|
+
for (const change of inner)
|
|
436
|
+
into.push(change, BREAK);
|
|
437
|
+
}
|
|
422
438
|
}
|
|
423
439
|
continue;
|
|
424
440
|
}
|
|
@@ -732,6 +748,11 @@ export class Body {
|
|
|
732
748
|
this.content.length = 0;
|
|
733
749
|
this.content.push(sdt);
|
|
734
750
|
linkParents(sdt, this.container);
|
|
751
|
+
declareSdtExtensions(this, sdt);
|
|
752
|
+
// under tracking the control itself is the insertion, its content what it was (check 30)
|
|
753
|
+
const tracker = this.changeTracker;
|
|
754
|
+
if (tracker)
|
|
755
|
+
markHolderInserted(tracker, sdt, this.content, () => { });
|
|
735
756
|
return new ContentControl(sdt, this.content, this);
|
|
736
757
|
}
|
|
737
758
|
}
|
|
@@ -104,4 +104,10 @@ export declare function addCommentEntry(body: Body, entry: CommentEntry): Promis
|
|
|
104
104
|
* order, replies nested under their parent (a reply is in the array only when its parent is not).
|
|
105
105
|
*/
|
|
106
106
|
export declare function commentsOf(scope: Body | Paragraph | Range): Promise<Comment[]>;
|
|
107
|
+
/**
|
|
108
|
+
* Comments whose anchored text went with a rejected insertion or an accepted deletion (check 32;
|
|
109
|
+
* `TrackedChange`'s `removeRevision`) had their markers removed then; their entries go here, with
|
|
110
|
+
* their replies, when the comments are next read or the package is saved (`WordprocessingMLPackage.saveTo`).
|
|
111
|
+
*/
|
|
112
|
+
export declare function flushPendingCommentRemovals(body: Body): Promise<void>;
|
|
107
113
|
export type { CommentParts, Author } from './comments.mjs';
|
|
@@ -301,11 +301,10 @@ function parentOf(element) {
|
|
|
301
301
|
return element.value.PARENT;
|
|
302
302
|
}
|
|
303
303
|
/**
|
|
304
|
-
* Where a marker for this segment's run goes
|
|
305
|
-
* the run
|
|
306
|
-
* insertion of its own (CR-002 section 13).
|
|
307
|
-
*
|
|
308
|
-
* it, which is what Word shows anyway once the insertion is accepted.
|
|
304
|
+
* Where a marker for this segment's run goes when it is not put inside the revision the run sits
|
|
305
|
+
* in (`placeAround`): beside the run, but outside the `w:ins` or `w:del`, so that a comment made
|
|
306
|
+
* while change tracking is on is a comment and not an insertion of its own (CR-002 section 13).
|
|
307
|
+
* Accepting or rejecting the revision then leaves the comment where it is.
|
|
309
308
|
*/
|
|
310
309
|
function markerSite(seg, fallback, after) {
|
|
311
310
|
var _a;
|
|
@@ -339,10 +338,29 @@ function placeAround(range, id) {
|
|
|
339
338
|
return;
|
|
340
339
|
}
|
|
341
340
|
const reference = referenceRun(id);
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
341
|
+
const first = covered[0];
|
|
342
|
+
const last = covered[covered.length - 1];
|
|
343
|
+
// A comment on part of an insertion, as Word writes it (the editor's measure-word2010.docx and
|
|
344
|
+
// measure-word15.docx, 2026-09-30; CR-002 section 38): the range start inside the w:ins before the
|
|
345
|
+
// first commented run, the w:ins closed at the range's end - the rest of it a second w:ins with a
|
|
346
|
+
// new id - and the end marker and the plain reference run after it. So the anchor stays narrow
|
|
347
|
+
// and the reference is no insertion. Before, both markers went outside the w:ins, which widened
|
|
348
|
+
// the anchor to the whole insertion. A deletion or a move keeps the markers outside (a deletion's
|
|
349
|
+
// form is not measured; a move's halves are not to be split).
|
|
350
|
+
const inInsertion = (seg) => { var _a; return ((_a = seg.revision) === null || _a === void 0 ? void 0 : _a.kind) === 'ins'; };
|
|
351
|
+
if (inInsertion(last)) {
|
|
352
|
+
paragraph.splitHolderAround(last.revision, last.runIndex + 1, [end, reference], () => paragraph.nextAnnotationId());
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
const after = markerSite(last, paragraph.p, true);
|
|
356
|
+
insertAt(after.owner, after.index, [end, reference], after.parent);
|
|
357
|
+
}
|
|
358
|
+
if (inInsertion(first))
|
|
359
|
+
insertAt(first.runOwner, first.runIndex, [start], first.revision.value);
|
|
360
|
+
else {
|
|
361
|
+
const before = markerSite(first, paragraph.p, false);
|
|
362
|
+
insertAt(before.owner, before.index, [start], before.parent);
|
|
363
|
+
}
|
|
346
364
|
}
|
|
347
365
|
/** Writes the markers of a reply next to its parent's, as Word nests them. */
|
|
348
366
|
function placeAfter(body, parentId, id) {
|
|
@@ -500,12 +518,59 @@ function paragraphsOfContent(content) {
|
|
|
500
518
|
* order, replies nested under their parent (a reply is in the array only when its parent is not).
|
|
501
519
|
*/
|
|
502
520
|
export async function commentsOf(scope) {
|
|
503
|
-
var _a, _b;
|
|
504
521
|
const paragraph = scope instanceof Range ? scope.paragraph : scope instanceof Paragraph ? scope : undefined;
|
|
505
522
|
const body = paragraph ? paragraph.parentBody : scope;
|
|
506
523
|
const parts = await commentPartsAccess().load(body);
|
|
507
524
|
if (!parts)
|
|
508
525
|
return [];
|
|
526
|
+
await flushPendingRemovals(body, parts);
|
|
527
|
+
const views = viewsOf(parts, body);
|
|
528
|
+
const ids = scope instanceof Range ? idsInRange(scope) : paragraph ? unique(markersOfParagraph(paragraph.element, paragraph.container).map((m) => m.id)) : commentIdsOf(body.container);
|
|
529
|
+
const inScope = new Set(ids);
|
|
530
|
+
const out = [];
|
|
531
|
+
for (const id of ids) {
|
|
532
|
+
const view = views.get(id);
|
|
533
|
+
if (!view)
|
|
534
|
+
continue;
|
|
535
|
+
if (view.parent && inScope.has(view.parent.id))
|
|
536
|
+
continue;
|
|
537
|
+
out.push(view);
|
|
538
|
+
}
|
|
539
|
+
return out;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Comments whose anchored text went with a rejected insertion or an accepted deletion (check 32;
|
|
543
|
+
* `TrackedChange`'s `removeRevision`) had their markers removed then; their entries go here, with
|
|
544
|
+
* their replies, when the comments are next read or the package is saved (`WordprocessingMLPackage.saveTo`).
|
|
545
|
+
*/
|
|
546
|
+
export async function flushPendingCommentRemovals(body) {
|
|
547
|
+
var _a;
|
|
548
|
+
const pkg = body.package_;
|
|
549
|
+
if (!((_a = pkg === null || pkg === void 0 ? void 0 : pkg.pendingCommentRemovals) === null || _a === void 0 ? void 0 : _a.size))
|
|
550
|
+
return;
|
|
551
|
+
const parts = await commentPartsAccess().load(body);
|
|
552
|
+
if (!parts) {
|
|
553
|
+
pkg.pendingCommentRemovals.clear();
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
await flushPendingRemovals(body, parts);
|
|
557
|
+
}
|
|
558
|
+
async function flushPendingRemovals(body, parts) {
|
|
559
|
+
const pkg = body.package_;
|
|
560
|
+
const pending = pkg === null || pkg === void 0 ? void 0 : pkg.pendingCommentRemovals;
|
|
561
|
+
if (!(pending === null || pending === void 0 ? void 0 : pending.size))
|
|
562
|
+
return;
|
|
563
|
+
const views = viewsOf(parts, body);
|
|
564
|
+
for (const id of [...pending]) {
|
|
565
|
+
const view = views.get(id);
|
|
566
|
+
if (view)
|
|
567
|
+
await view.delete();
|
|
568
|
+
pending.delete(id);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/** A `Comment` view per entry in the parts, replies linked to their parents. */
|
|
572
|
+
function viewsOf(parts, body) {
|
|
573
|
+
var _a, _b;
|
|
509
574
|
const views = new Map();
|
|
510
575
|
for (const comment of (_a = parts.comments.comment) !== null && _a !== void 0 ? _a : []) {
|
|
511
576
|
if (typeof comment.id === 'number' && !views.has(comment.id))
|
|
@@ -523,18 +588,7 @@ export async function commentsOf(scope) {
|
|
|
523
588
|
if (parent && parent !== view)
|
|
524
589
|
parent.addReply(view);
|
|
525
590
|
}
|
|
526
|
-
|
|
527
|
-
const inScope = new Set(ids);
|
|
528
|
-
const out = [];
|
|
529
|
-
for (const id of ids) {
|
|
530
|
-
const view = views.get(id);
|
|
531
|
-
if (!view)
|
|
532
|
-
continue;
|
|
533
|
-
if (view.parent && inScope.has(view.parent.id))
|
|
534
|
-
continue;
|
|
535
|
-
out.push(view);
|
|
536
|
-
}
|
|
537
|
-
return out;
|
|
591
|
+
return views;
|
|
538
592
|
}
|
|
539
593
|
/** The comments a range touches: a comment whose markers overlap [start, end]. */
|
|
540
594
|
function idsInRange(range) {
|
|
@@ -94,6 +94,12 @@ export declare class ContentControl {
|
|
|
94
94
|
private parentParagraph;
|
|
95
95
|
/** The offsets a run-level control covers in its paragraph. */
|
|
96
96
|
private runSpan;
|
|
97
|
+
/**
|
|
98
|
+
* Text into a run control that holds no run: a new run in its content, in a `w:ins` under
|
|
99
|
+
* tracking (Word: check 30, new-control-typed), placed before the control's trailing insertion
|
|
100
|
+
* marker when it is itself an insertion.
|
|
101
|
+
*/
|
|
102
|
+
private insertIntoEmpty;
|
|
97
103
|
/** The control's data binding (Office JS `xmlMapping`), backed by `w:dataBinding`. */
|
|
98
104
|
get xmlMapping(): XmlMapping;
|
|
99
105
|
/**
|
|
@@ -134,7 +140,11 @@ export declare class ContentControl {
|
|
|
134
140
|
get repeatingSectionContentControl(): RepeatingSectionContentControl | undefined;
|
|
135
141
|
/** The group view, for a `w:group` control (Office JS: an object with no members of its own). */
|
|
136
142
|
get groupContentControl(): GroupContentControl | undefined;
|
|
137
|
-
/**
|
|
143
|
+
/**
|
|
144
|
+
* Adds or replaces a `w:sdtPr` child (extension). A `w15` element (`w15:appearance`, `w15:color`,
|
|
145
|
+
* `w15:dataBinding`, the repeating section) puts `w15` in the part's `mc:Ignorable`, as Word's own
|
|
146
|
+
* files have it: Word 2010 opens a part holding one only then (CR-002 section 40).
|
|
147
|
+
*/
|
|
138
148
|
putProperty(element: Element): void;
|
|
139
149
|
/** Removes a `w:sdtPr` child (extension). */
|
|
140
150
|
removeProperty(localPart: string, namespaceURI?: string): void;
|
|
@@ -6,9 +6,10 @@ import { marshalString, deepCopy } from '@docx4j/generated-objects-ts';
|
|
|
6
6
|
// nothing from here at run time (types only), so this direction is the only one.
|
|
7
7
|
import { XmlMapping } from '../customxml/XmlMapping.mjs';
|
|
8
8
|
import { CheckboxContentControl, DatePickerContentControl, ListContentControl, PictureContentControl, RepeatingSectionContentControl, GroupContentControl, checkboxRun, CHECKBOX_FONT, } from '../customxml/kinds.mjs';
|
|
9
|
-
import { runsForValue, updateFromControl, PLACEHOLDER_TEXT } from '../customxml/bindings.mjs';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { runsForValue, updateFromControl, PLACEHOLDER_TEXT, PLACEHOLDER_STYLE } from '../customxml/bindings.mjs';
|
|
10
|
+
import { declareIgnorable } from './tracking.mjs';
|
|
11
|
+
import { sdtProperty, sdtKindOf, t as textItem } from '@docx4j/generated-objects-ts/builders/wml';
|
|
12
|
+
import { typeNameOf, childrenOf, textOf, runItemsOf, segmentsOf, linkParents, runOf, SDT_TYPES } from './tree.mjs';
|
|
12
13
|
import { Range } from './Range.mjs';
|
|
13
14
|
import { Table } from './Table.mjs';
|
|
14
15
|
/** The namespaces of the Word 2010 and 2012 content-control properties. */
|
|
@@ -128,6 +129,8 @@ export class ContentControl {
|
|
|
128
129
|
if (!span)
|
|
129
130
|
throw new Docx4JException('This content control is not in a paragraph');
|
|
130
131
|
const { paragraph, start, end } = span;
|
|
132
|
+
if (span.empty)
|
|
133
|
+
return this.insertIntoEmpty(paragraph, text);
|
|
131
134
|
if (location === 'Start')
|
|
132
135
|
return paragraph.splice(start, start, text);
|
|
133
136
|
if (location === 'End')
|
|
@@ -251,7 +254,8 @@ export class ContentControl {
|
|
|
251
254
|
};
|
|
252
255
|
let start;
|
|
253
256
|
let end = 0;
|
|
254
|
-
|
|
257
|
+
const segs = segmentsOf(paragraph.p);
|
|
258
|
+
for (const seg of segs) {
|
|
255
259
|
if (!inside(seg.run))
|
|
256
260
|
continue;
|
|
257
261
|
if (start === undefined)
|
|
@@ -259,11 +263,54 @@ export class ContentControl {
|
|
|
259
263
|
end = seg.end;
|
|
260
264
|
}
|
|
261
265
|
if (start === undefined) {
|
|
262
|
-
//
|
|
263
|
-
|
|
266
|
+
// a control holding no text: its position is the end of the last text before it in the
|
|
267
|
+
// paragraph (before, it was reported at 0, and text put into it went to the paragraph's start)
|
|
268
|
+
let pos = 0;
|
|
269
|
+
let found = false;
|
|
270
|
+
const visit = (items) => {
|
|
271
|
+
for (const el of items !== null && items !== void 0 ? items : []) {
|
|
272
|
+
if (found)
|
|
273
|
+
return;
|
|
274
|
+
if (el === this.element) {
|
|
275
|
+
found = true;
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const v = el.value;
|
|
279
|
+
if (typeof v !== 'object' || v === null)
|
|
280
|
+
continue;
|
|
281
|
+
if (typeNameOf(el) === 'org_docx4j_wml.R') {
|
|
282
|
+
const last = segs.filter((seg) => seg.run === v).pop();
|
|
283
|
+
if (last)
|
|
284
|
+
pos = last.end;
|
|
285
|
+
}
|
|
286
|
+
else
|
|
287
|
+
visit(runItemsOf(v));
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
visit(paragraph.p.content);
|
|
291
|
+
return { paragraph, start: pos, end: pos, empty: true };
|
|
264
292
|
}
|
|
265
293
|
return { paragraph, start, end };
|
|
266
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* Text into a run control that holds no run: a new run in its content, in a `w:ins` under
|
|
297
|
+
* tracking (Word: check 30, new-control-typed), placed before the control's trailing insertion
|
|
298
|
+
* marker when it is itself an insertion.
|
|
299
|
+
*/
|
|
300
|
+
insertIntoEmpty(paragraph, text) {
|
|
301
|
+
var _a, _b;
|
|
302
|
+
var _c;
|
|
303
|
+
const sdt = this.sdt;
|
|
304
|
+
const items = ((_b = (_c = ((_a = sdt.sdtContent) !== null && _a !== void 0 ? _a : (sdt.sdtContent = {}))).content) !== null && _b !== void 0 ? _b : (_c.content = []));
|
|
305
|
+
const run = runOf([textItem(text)]);
|
|
306
|
+
const tracker = paragraph.changeTracker;
|
|
307
|
+
const item = tracker ? tracker.ins([run]) : run;
|
|
308
|
+
const at = items.findIndex((el) => { var _a; return ((_a = el.name) === null || _a === void 0 ? void 0 : _a.localPart) === 'customXmlInsRangeStart'; });
|
|
309
|
+
items.splice(at < 0 ? items.length : at, 0, item);
|
|
310
|
+
linkParents(item, sdt.sdtContent);
|
|
311
|
+
const span = this.runSpan();
|
|
312
|
+
return new Range(paragraph, span.start, span.end);
|
|
313
|
+
}
|
|
267
314
|
// --- CR-002 phase E: the XML mapping, the typed kinds and the w:sdtPr properties ---
|
|
268
315
|
/** The control's data binding (Office JS `xmlMapping`), backed by `w:dataBinding`. */
|
|
269
316
|
get xmlMapping() {
|
|
@@ -383,7 +430,11 @@ export class ContentControl {
|
|
|
383
430
|
get groupContentControl() {
|
|
384
431
|
return sdtProperty(this.sdt.sdtPr, 'group') ? new GroupContentControl() : undefined;
|
|
385
432
|
}
|
|
386
|
-
/**
|
|
433
|
+
/**
|
|
434
|
+
* Adds or replaces a `w:sdtPr` child (extension). A `w15` element (`w15:appearance`, `w15:color`,
|
|
435
|
+
* `w15:dataBinding`, the repeating section) puts `w15` in the part's `mc:Ignorable`, as Word's own
|
|
436
|
+
* files have it: Word 2010 opens a part holding one only then (CR-002 section 40).
|
|
437
|
+
*/
|
|
387
438
|
putProperty(element) {
|
|
388
439
|
var _a, _b;
|
|
389
440
|
var _c;
|
|
@@ -395,6 +446,8 @@ export class ContentControl {
|
|
|
395
446
|
items[at] = element;
|
|
396
447
|
else
|
|
397
448
|
items.push(element);
|
|
449
|
+
if (element.name.namespaceURI === W15_NS)
|
|
450
|
+
declareIgnorable(this.parentBody.part, 'w15');
|
|
398
451
|
}
|
|
399
452
|
/** Removes a `w:sdtPr` child (extension). */
|
|
400
453
|
removeProperty(localPart, namespaceURI) {
|
|
@@ -435,11 +488,11 @@ export class ContentControl {
|
|
|
435
488
|
* the runs of a run-level one.
|
|
436
489
|
*/
|
|
437
490
|
setBoundContent(runs, showingPlaceholder = false) {
|
|
438
|
-
var _a, _b;
|
|
439
|
-
var
|
|
491
|
+
var _a, _b, _c, _d;
|
|
492
|
+
var _e;
|
|
440
493
|
const sdt = this.sdt;
|
|
441
494
|
(_a = sdt.sdtContent) !== null && _a !== void 0 ? _a : (sdt.sdtContent = {});
|
|
442
|
-
const content = ((_b = (
|
|
495
|
+
const content = ((_b = (_e = sdt.sdtContent).content) !== null && _b !== void 0 ? _b : (_e.content = []));
|
|
443
496
|
const first = content.find((e) => typeNameOf(e) !== undefined);
|
|
444
497
|
if (this.form === 'Run' || (first && typeNameOf(first) === 'org_docx4j_wml.R')) {
|
|
445
498
|
content.length = 0;
|
|
@@ -459,6 +512,12 @@ export class ContentControl {
|
|
|
459
512
|
}
|
|
460
513
|
}
|
|
461
514
|
this.isShowingPlaceholder = showingPlaceholder;
|
|
515
|
+
// The placeholder run names the PlaceholderText style, which a document made here lacks: the
|
|
516
|
+
// package splices the definition in when it is saved, as it does a new hyperlink's style. Word
|
|
517
|
+
// 2010 drops a w:rStyle naming a style the part does not define (CR-002 section 40).
|
|
518
|
+
if (showingPlaceholder) {
|
|
519
|
+
(_d = (_c = this.parentBody.package_) === null || _c === void 0 ? void 0 : _c.requireStyles) === null || _d === void 0 ? void 0 : _d.call(_c, [PLACEHOLDER_STYLE]);
|
|
520
|
+
}
|
|
462
521
|
}
|
|
463
522
|
/** Shows a checkbox's glyph, as Word writes it: one run in the checkbox font (docx4j checkboxRun). */
|
|
464
523
|
setCheckboxGlyph(symbol) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
|
|
2
|
+
import type { CellContext } from '../properties/table.mjs';
|
|
2
3
|
import { type Element, W_NS, type TextSegment, type TextViewOptions } from './tree.mjs';
|
|
3
4
|
import { Font, type FontTracking } from './Font.mjs';
|
|
4
5
|
import { Range } from './Range.mjs';
|
|
@@ -95,6 +96,16 @@ export declare class Paragraph {
|
|
|
95
96
|
get effectivePPr(): wml.PPr;
|
|
96
97
|
/** The paragraph mark's effective run properties (docx4j `getEffectiveParagraphMarkRPr`). */
|
|
97
98
|
get effectiveParagraphMarkRPr(): wml.RPr;
|
|
99
|
+
/**
|
|
100
|
+
* Where this paragraph sits in a table, as far as its formatting goes (docx4j
|
|
101
|
+
* `PropertyResolver.cellContextOf`; CR-007): the table style its table resolves to and the
|
|
102
|
+
* conditional formats it is under. Undefined outside a table, and in a text box, note or comment
|
|
103
|
+
* reached from a cell. Every effective read of the paragraph and of its runs passes it, so a run
|
|
104
|
+
* in a header row the table style makes bold reads bold. Each read walks the table's rows; code
|
|
105
|
+
* resolving every paragraph of a large table should walk with a `TableContextTracker` instead.
|
|
106
|
+
* Needs the package's resolver, as the effective reads do.
|
|
107
|
+
*/
|
|
108
|
+
get cellContext(): CellContext | undefined;
|
|
98
109
|
/** The `w:pPr` a formatting read works from: the resolved one, or this paragraph's own. */
|
|
99
110
|
private readPPr;
|
|
100
111
|
/**
|
|
@@ -106,6 +117,15 @@ export declare class Paragraph {
|
|
|
106
117
|
/** The effective alignment; an absent `w:jc` after resolution is `'Left'`, as Office JS. */
|
|
107
118
|
get alignment(): Alignment;
|
|
108
119
|
set alignment(v: AlignmentOrUnknown);
|
|
120
|
+
/**
|
|
121
|
+
* The alignment the paragraph resolves to with no `w:jc` of its own: its style's, else the
|
|
122
|
+
* defaults'. Word's Ctrl+L on a centred paragraph whose style is left-aligned removes the `w:jc`
|
|
123
|
+
* rather than writing `w:jc="left"`, with tracking on (check 26) and off (the editor's
|
|
124
|
+
* `measure-word2010.docx` and `measure-word15.docx`, 2026-09-30; CR-002 section 38), so a value the
|
|
125
|
+
* paragraph already inherits is not written. Measured for left over a left-aligned style; taken to
|
|
126
|
+
* hold for any value. Undefined without a resolver, where the value is written as asked.
|
|
127
|
+
*/
|
|
128
|
+
private inheritedAlignment;
|
|
109
129
|
/** Indents and spacing in points, as Office JS; the effective values (CR-001 Phase B step 2). */
|
|
110
130
|
get leftIndent(): number;
|
|
111
131
|
set leftIndent(pt: number);
|
|
@@ -241,7 +261,7 @@ export declare class Paragraph {
|
|
|
241
261
|
* is removed, a run left empty is removed. Text is inserted into the w:t at `start`,
|
|
242
262
|
* else into a new run with the formatting of the nearest run.
|
|
243
263
|
*/
|
|
244
|
-
splice(start: number, end: number, text: string): Range;
|
|
264
|
+
splice(start: number, end: number, text: string, prefer?: 'before' | 'after'): Range;
|
|
245
265
|
/**
|
|
246
266
|
* Inserts run-level items (runs, hyperlinks, content controls) at a text offset, splitting the
|
|
247
267
|
* run there when the offset falls inside one. PARENT is linked on what is inserted.
|
|
@@ -309,6 +329,14 @@ export declare class Paragraph {
|
|
|
309
329
|
* then does what it did before.
|
|
310
330
|
*/
|
|
311
331
|
private insertBetweenHalves;
|
|
332
|
+
/**
|
|
333
|
+
* Moves a revision's items from `index` on into a copy of it - its attributes, a new `w:id` - and
|
|
334
|
+
* puts `item` between the two; with nothing from `index` on, `item` just goes after the revision.
|
|
335
|
+
* False, with nothing changed, where the revision's items cannot be told apart from its attributes.
|
|
336
|
+
* Internal: `Range.insertContentControl` uses it to put a new control between the halves of the
|
|
337
|
+
* insertion it wraps part of (check 30).
|
|
338
|
+
*/
|
|
339
|
+
splitHolderAround(holder: RevisionHolderOf, index: number, item: Element | Element[], nextId: () => number): boolean;
|
|
312
340
|
/**
|
|
313
341
|
* Puts `item` beside a revision rather than in it: after it, or before it - and then before the
|
|
314
342
|
* range start of a move whose range opens just there, so that text typed at the start of moved
|
|
@@ -320,13 +348,19 @@ export declare class Paragraph {
|
|
|
320
348
|
* is a package that keeps one, since a document has one id space; else one above every id in the
|
|
321
349
|
* story.
|
|
322
350
|
*/
|
|
323
|
-
|
|
351
|
+
/** @internal `Comment` uses it to split an insertion at a comment's end. */
|
|
352
|
+
nextAnnotationId(): number;
|
|
324
353
|
/**
|
|
325
354
|
* Text inserted at `at` as a `w:ins`. A run of this author's own that is already inside a
|
|
326
355
|
* `w:ins` is extended rather than nested in another one, as Word does. `anchor` is the `w:del`
|
|
327
356
|
* of a replacement, which the insertion must follow.
|
|
328
357
|
*/
|
|
329
358
|
private insertTracked;
|
|
359
|
+
/**
|
|
360
|
+
* An insertion put at `at`, `before` and `after` being the segments either side of it once the runs
|
|
361
|
+
* are split there: never inside another's revision.
|
|
362
|
+
*/
|
|
363
|
+
private placeInsertion;
|
|
330
364
|
/**
|
|
331
365
|
* Splits runs so that every run holding text in [start, end) holds nothing else, and returns
|
|
332
366
|
* the segments inside the span. `splitAt` does the `w:t` boundaries; this also moves the items
|
|
@@ -338,4 +372,5 @@ export declare class Paragraph {
|
|
|
338
372
|
/** Moves the items after `seg` into a run of their own, behind `seg`'s run. */
|
|
339
373
|
private splitRunAfter;
|
|
340
374
|
}
|
|
375
|
+
type RevisionHolderOf = NonNullable<TextSegment['revision']>;
|
|
341
376
|
export { W_NS };
|