@docx4j/core-ts 0.3.2 → 0.5.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.
- package/README.md +8 -3
- package/dist/html/convert.d.mts +116 -0
- package/dist/html/convert.mjs +565 -0
- package/dist/html/elements.d.mts +19 -0
- package/dist/html/elements.mjs +182 -0
- package/dist/html/index.d.mts +2 -0
- package/dist/html/index.mjs +4 -0
- package/dist/model/content/Body.d.mts +13 -1
- package/dist/model/content/Body.mjs +97 -19
- package/dist/model/content/Comment.mjs +7 -0
- package/dist/model/content/InlinePicture.d.mts +89 -10
- package/dist/model/content/InlinePicture.mjs +245 -35
- package/dist/model/content/List.mjs +10 -1
- package/dist/model/content/Paragraph.d.mts +28 -5
- package/dist/model/content/Paragraph.mjs +77 -37
- package/dist/model/content/Range.d.mts +18 -2
- package/dist/model/content/Range.mjs +194 -64
- package/dist/model/content/Table.d.mts +38 -0
- package/dist/model/content/Table.mjs +80 -2
- package/dist/model/content/TrackedChange.d.mts +129 -3
- package/dist/model/content/TrackedChange.mjs +577 -12
- package/dist/model/content/binder.d.mts +2 -1
- package/dist/model/content/binder.mjs +48 -1
- package/dist/model/content/index.d.mts +4 -3
- package/dist/model/content/index.mjs +3 -2
- package/dist/model/content/relationships.d.mts +30 -0
- package/dist/model/content/relationships.mjs +133 -0
- package/dist/model/content/stylesFacade.d.mts +4 -2
- package/dist/model/content/stylesFacade.mjs +20 -6
- package/dist/model/content/tableColumns.d.mts +48 -0
- package/dist/model/content/tableColumns.mjs +484 -0
- package/dist/model/content/tree.d.mts +52 -2
- package/dist/model/content/tree.mjs +48 -3
- package/dist/model/customxml/CustomXmlPartCollection.d.mts +6 -1
- package/dist/model/customxml/CustomXmlPartCollection.mjs +19 -3
- package/dist/model/customxml/bindings.d.mts +5 -2
- package/dist/model/customxml/bindings.mjs +21 -6
- package/dist/model/customxml/index.d.mts +3 -0
- package/dist/model/customxml/index.mjs +3 -0
- package/dist/model/customxml/opendope.d.mts +32 -0
- package/dist/model/customxml/opendope.mjs +56 -0
- package/dist/model/customxml/pictures.d.mts +57 -0
- package/dist/model/customxml/pictures.mjs +192 -0
- package/dist/model/customxml/xhtml.d.mts +42 -0
- package/dist/model/customxml/xhtml.mjs +162 -0
- package/dist/model/fonts/families.generated.mjs +2 -2
- package/dist/model/fonts/registry.mjs +7 -1
- package/dist/model/fonts/substitutions.generated.mjs +2 -2
- package/dist/model/fonts/themes.generated.mjs +1 -1
- package/dist/model/listnumbering/definitions.mjs +18 -0
- package/dist/model/properties/PropertyResolver.d.mts +27 -6
- package/dist/model/properties/PropertyResolver.mjs +83 -16
- package/dist/model/properties/catalogue.d.mts +15 -1
- package/dist/model/properties/catalogue.mjs +80 -27
- package/dist/model/properties/table.d.mts +16 -2
- package/dist/model/properties/table.mjs +13 -3
- package/dist/opc/Load.d.mts +9 -2
- package/dist/opc/index.d.mts +2 -2
- package/dist/opc/index.mjs +2 -2
- package/dist/opc/mce/preprocessor.d.mts +62 -6
- package/dist/opc/mce/preprocessor.mjs +142 -35
- package/dist/opc/mce/understood.d.mts +10 -0
- package/dist/opc/mce/understood.mjs +25 -3
- package/dist/packages/OpcPackage.d.mts +7 -0
- package/dist/packages/OpcPackage.mjs +15 -3
- package/dist/packages/WordprocessingMLPackage.d.mts +35 -2
- package/dist/packages/WordprocessingMLPackage.mjs +99 -2
- package/dist/parts/XmlPart.d.mts +26 -5
- package/dist/parts/XmlPart.mjs +67 -18
- package/dist/parts/wml/knownStyles.d.mts +1 -0
- package/dist/parts/wml/knownStyles.mjs +2233 -0
- package/package.json +7 -2
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { inlinePicture } from '@docx4j/generated-objects-ts/builders/wml';
|
|
2
2
|
import { Docx4JException } from '../../opc/exceptions.mjs';
|
|
3
3
|
import { ContentTypes, IMAGE_CONTENT_TYPES_BY_EXTENSION } from '../../opc/ContentTypes.mjs';
|
|
4
|
-
import { base64Decode, base64Encode } from '../../xml/dom.mjs';
|
|
4
|
+
import { base64Decode, base64Encode, decodeXmlText } from '../../xml/dom.mjs';
|
|
5
5
|
import { ImagePart } from '../../parts/BinaryPart.mjs';
|
|
6
6
|
import { PartName } from '../../opc/PartName.mjs';
|
|
7
|
-
import { find, runOf } from './tree.mjs';
|
|
7
|
+
import { find, runOf, linkParents, revisionKindOf, isElement, typeNameOf, isAlternateContent, selectedBranchOf } from './tree.mjs';
|
|
8
|
+
import { copyRPr } from './tracking.mjs';
|
|
9
|
+
import { relationshipIdsIn, queueRelationshipRemovals } from './relationships.mjs';
|
|
8
10
|
/** English Metric Units per inch, and per twip (docx4j UnitsOfMeasurement). */
|
|
9
11
|
const EMU_PER_INCH = 914400;
|
|
10
12
|
const EMU_PER_TWIP = 635;
|
|
@@ -15,7 +17,9 @@ const EMU_PER_POINT = 12700;
|
|
|
15
17
|
* The size and kind of an image, from its header: PNG (IHDR and pHYs), JPEG (SOFn and the JFIF
|
|
16
18
|
* APP0 densities), GIF (the logical screen descriptor) and BMP (the DIB header, with its pixels
|
|
17
19
|
* per metre). docx4j reads these through XML Graphics Commons' ImageInfo; this is the same
|
|
18
|
-
* information, and the same 96 dpi default when a file declares none.
|
|
20
|
+
* information, and the same 96 dpi default when a file declares none. An SVG document is told by
|
|
21
|
+
* its root element and has no pixel size (both 0): it can be the part behind an existing picture
|
|
22
|
+
* (CR-005 section 9, a binding that keeps the drawing) but not be placed as a new one.
|
|
19
23
|
*/
|
|
20
24
|
export function imageInfoOf(bytes) {
|
|
21
25
|
const png = pngInfo(bytes);
|
|
@@ -30,8 +34,27 @@ export function imageInfoOf(bytes) {
|
|
|
30
34
|
const bmp = bmpInfo(bytes);
|
|
31
35
|
if (bmp)
|
|
32
36
|
return bmp;
|
|
37
|
+
const svg = svgInfo(bytes);
|
|
38
|
+
if (svg)
|
|
39
|
+
return svg;
|
|
33
40
|
const head = Array.from(bytes.subarray(0, 8)).map((b) => b.toString(16).padStart(2, '0')).join(' ');
|
|
34
|
-
throw new Docx4JException(`Unsupported image format (first bytes ${head}); PNG, JPEG, GIF and
|
|
41
|
+
throw new Docx4JException(`Unsupported image format (first bytes ${head}); PNG, JPEG, GIF, BMP and SVG are read here`);
|
|
42
|
+
}
|
|
43
|
+
/** An SVG document: an XML declaration, comments and a doctype allowed before the `svg` root. */
|
|
44
|
+
function svgInfo(b) {
|
|
45
|
+
if (b.length < 5)
|
|
46
|
+
return undefined;
|
|
47
|
+
let head;
|
|
48
|
+
try {
|
|
49
|
+
head = decodeXmlText(b.subarray(0, Math.min(b.length, 4096)));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const body = head.replace(/^\uFEFF/, '').replace(/<\?xml[^>]*\?>/, '').replace(/<!--[\s\S]*?-->/g, '').replace(/<!DOCTYPE[^>]*>/i, '').trimStart();
|
|
55
|
+
if (!/^<(\w+:)?svg[\s>]/.test(body))
|
|
56
|
+
return undefined;
|
|
57
|
+
return { contentType: ContentTypes.IMAGE_SVG, extension: 'svg', widthPx: 0, heightPx: 0, dpiX: DEFAULT_DPI, dpiY: DEFAULT_DPI };
|
|
35
58
|
}
|
|
36
59
|
function be16(b, at) { return (b[at] << 8) | b[at + 1]; }
|
|
37
60
|
function be32(b, at) { return ((b[at] << 24) >>> 0) + (b[at + 1] << 16) + (b[at + 2] << 8) + b[at + 3]; }
|
|
@@ -144,11 +167,12 @@ export function naturalSizeEmu(info) {
|
|
|
144
167
|
}
|
|
145
168
|
/**
|
|
146
169
|
* Adds the image as a part of `source` (the main document part, or the header or footer the body
|
|
147
|
-
* belongs to) with a relationship, and
|
|
148
|
-
* `
|
|
170
|
+
* belongs to) with a relationship, and nothing else: docx4j `BinaryPartAbstractImage.createImagePart`
|
|
171
|
+
* (`BindingTraverserXSLT.createImagePartReturnRelId`, where a picture binding replaces an `r:embed`
|
|
172
|
+
* and keeps the drawing, CR-005 section 9). Throws, and adds nothing, when the bytes are not an
|
|
173
|
+
* image this package reads.
|
|
149
174
|
*/
|
|
150
|
-
export function
|
|
151
|
-
var _a, _b;
|
|
175
|
+
export function addImagePart(source, base64) {
|
|
152
176
|
const pkg = source.package;
|
|
153
177
|
if (!pkg)
|
|
154
178
|
throw new Docx4JException('This part is not in a package yet; add it before adding an image');
|
|
@@ -157,6 +181,21 @@ export function addImage(source, base64, scope, maxCx, options = {}) {
|
|
|
157
181
|
const imagePart = new ImagePart(freeImageName(pkg, info.extension), info.contentType);
|
|
158
182
|
imagePart.setBytes(bytes);
|
|
159
183
|
const rel = source.addTargetPart(imagePart);
|
|
184
|
+
return { imagePart, relId: rel.id, info };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Adds the image as a part of `source` with a relationship (`addImagePart`), and builds the run
|
|
188
|
+
* that shows it. docx4j: `BinaryPartAbstractImage.createImagePart` then `createImageInline`.
|
|
189
|
+
*/
|
|
190
|
+
export function addImage(source, base64, scope, maxCx, options = {}) {
|
|
191
|
+
var _a, _b;
|
|
192
|
+
const bytes = base64Decode(base64);
|
|
193
|
+
const info = imageInfoOf(bytes);
|
|
194
|
+
if (info.widthPx === 0 || info.heightPx === 0) {
|
|
195
|
+
throw new Docx4JException(`A ${info.extension.toUpperCase()} image has no pixel size to place it at; PNG, JPEG, GIF and BMP can be placed`);
|
|
196
|
+
}
|
|
197
|
+
const { imagePart, relId } = addImagePart(source, base64);
|
|
198
|
+
const rel = { id: relId };
|
|
160
199
|
let { cx, cy } = naturalSizeEmu(info);
|
|
161
200
|
if (options.width !== undefined) {
|
|
162
201
|
const wanted = Math.round(options.width * EMU_PER_POINT);
|
|
@@ -211,6 +250,57 @@ export function writableWidthEmu(container) {
|
|
|
211
250
|
const twips = pgSz.w - left - right;
|
|
212
251
|
return twips > 0 ? twips * EMU_PER_TWIP : undefined;
|
|
213
252
|
}
|
|
253
|
+
/** The TYPE_NAME of `asvg:svgBlip`, Office 2016's SVG picture extension of an `a:blip`. */
|
|
254
|
+
const SVG_BLIP = 'org_docx4j_com_microsoft_schemas_office_drawing_x2016_SVG_main.CTSVGBlip';
|
|
255
|
+
/** The `a:ext` URI that `asvg:svgBlip` stands under, and its namespace. */
|
|
256
|
+
const SVG_BLIP_EXT_URI = '{96DAC541-7B7A-43D3-8B79-37D633B846F1}';
|
|
257
|
+
const SVG_NS = 'http://schemas.microsoft.com/office/drawing/2016/SVG/main';
|
|
258
|
+
/** The `asvg:svgBlip` in a blip's `a:extLst`, if any: an SVG picture's SVG (CR-002 section 52). */
|
|
259
|
+
export function svgBlipOf(blip) {
|
|
260
|
+
var _a, _b;
|
|
261
|
+
for (const ext of (_b = (_a = blip === null || blip === void 0 ? void 0 : blip.extLst) === null || _a === void 0 ? void 0 : _a.ext) !== null && _b !== void 0 ? _b : []) {
|
|
262
|
+
const any = ext.any;
|
|
263
|
+
if (isElement(any) && typeNameOf(any) === SVG_BLIP)
|
|
264
|
+
return any.value;
|
|
265
|
+
}
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Points a blip at a new image the way Word's own picture binding does (test/README.md checks 46 and
|
|
270
|
+
* 47; CR-002 section 52). The blip's effects and other extensions stay.
|
|
271
|
+
* - A raster image goes in `r:embed`, and an `asvg:svgBlip` is taken away. Left in place, Word would go
|
|
272
|
+
* on drawing its old SVG, and would replace the new image with a PNG of that SVG when it saves.
|
|
273
|
+
* - An SVG goes under the `asvg:svgBlip`, and `r:embed` is taken away. Word puts a PNG of the SVG it
|
|
274
|
+
* makes itself in `r:embed`, which this package cannot make, and Word 365 draws the SVG without one.
|
|
275
|
+
* `r:link` is left as it was.
|
|
276
|
+
*/
|
|
277
|
+
export function setBlipImage(blip, relId, svg) {
|
|
278
|
+
var _a, _b;
|
|
279
|
+
if (!svg) {
|
|
280
|
+
blip.embed = relId;
|
|
281
|
+
const list = blip.extLst;
|
|
282
|
+
if (list === null || list === void 0 ? void 0 : list.ext) {
|
|
283
|
+
list.ext = list.ext.filter((ext) => !(isElement(ext.any) && typeNameOf(ext.any) === SVG_BLIP));
|
|
284
|
+
if (list.ext.length === 0)
|
|
285
|
+
delete blip.extLst;
|
|
286
|
+
}
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
delete blip.embed;
|
|
290
|
+
const existing = svgBlipOf(blip);
|
|
291
|
+
if (existing) {
|
|
292
|
+
existing.embed = relId;
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const list = ((_a = blip.extLst) !== null && _a !== void 0 ? _a : (blip.extLst = { TYPE_NAME: 'org_docx4j_dml.CTOfficeArtExtensionList', ext: [] }));
|
|
296
|
+
const ext = {
|
|
297
|
+
TYPE_NAME: 'org_docx4j_dml.CTOfficeArtExtension',
|
|
298
|
+
uri: SVG_BLIP_EXT_URI,
|
|
299
|
+
any: { name: { namespaceURI: SVG_NS, localPart: 'svgBlip' }, value: { TYPE_NAME: SVG_BLIP, embed: relId } },
|
|
300
|
+
};
|
|
301
|
+
((_b = list.ext) !== null && _b !== void 0 ? _b : (list.ext = [])).push(ext);
|
|
302
|
+
linkParents(list, blip);
|
|
303
|
+
}
|
|
214
304
|
const FORMAT_BY_CONTENT_TYPE = {
|
|
215
305
|
[ContentTypes.IMAGE_PNG]: 'Png', [ContentTypes.IMAGE_X_PNG]: 'Png', [ContentTypes.IMAGE_JPEG]: 'Jpeg',
|
|
216
306
|
[ContentTypes.IMAGE_GIF]: 'Gif', [ContentTypes.IMAGE_BMP]: 'Bmp', [ContentTypes.IMAGE_TIFF]: 'Tiff',
|
|
@@ -218,18 +308,48 @@ const FORMAT_BY_CONTENT_TYPE = {
|
|
|
218
308
|
[ContentTypes.IMAGE_WMF]: 'Wmf',
|
|
219
309
|
};
|
|
220
310
|
/**
|
|
221
|
-
*
|
|
222
|
-
*
|
|
311
|
+
* The inline pictures a run's item holds: the item itself when it is a `w:drawing` whose `wp:inline`
|
|
312
|
+
* holds a `pic:pic`, and those in a kept `mc:AlternateContent`'s selected branch (`selectedBranchOf`,
|
|
313
|
+
* CR-010 phase 3). A drawing of anything else, such as a chart, a text box, a group or SmartArt, is no
|
|
314
|
+
* inline picture: Office JS lists none for an inline text box (CR-010 section 12.6, test/README.md
|
|
315
|
+
* check 42).
|
|
316
|
+
*/
|
|
317
|
+
export function picturesIn(item) {
|
|
318
|
+
var _a;
|
|
319
|
+
if (isAlternateContent(item)) {
|
|
320
|
+
return ((_a = selectedBranchOf(item.value)) !== null && _a !== void 0 ? _a : []).flatMap((child) => (isElement(child) ? picturesIn(child) : []));
|
|
321
|
+
}
|
|
322
|
+
return item.name.localPart === 'drawing' && holdsInlinePicture(item.value) ? [item] : [];
|
|
323
|
+
}
|
|
324
|
+
/** True for a drawing whose `wp:inline` holds a `pic:pic`. */
|
|
325
|
+
function holdsInlinePicture(drawing) {
|
|
326
|
+
var _a, _b, _c, _d;
|
|
327
|
+
const inline = (_a = drawing.anchorOrInline) === null || _a === void 0 ? void 0 : _a[0];
|
|
328
|
+
if (!inline || inline.TYPE_NAME !== 'org_docx4j_dml_wordprocessingDrawing.Inline')
|
|
329
|
+
return false;
|
|
330
|
+
const first = (_d = (_c = (_b = inline.graphic) === null || _b === void 0 ? void 0 : _b.graphicData) === null || _c === void 0 ? void 0 : _c.any) === null || _d === void 0 ? void 0 : _d[0];
|
|
331
|
+
return isElement(first) && typeNameOf(first) === 'org_docx4j_dml_picture.Pic';
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* A subset of Office JS `Word.InlinePicture` over a `w:drawing` holding a `wp:inline` with a `pic:pic`.
|
|
335
|
+
* A view: the drawing, the run it is in and the paragraph. Sizes are points, as Office JS reports them.
|
|
336
|
+
*
|
|
337
|
+
* A picture in a kept `mc:AlternateContent`'s selected branch has the element as its `holder`. Reads and
|
|
338
|
+
* the setters are the selected branch's drawing; `delete` removes the element whole, every branch with
|
|
339
|
+
* it (CR-010 phase 3).
|
|
223
340
|
*/
|
|
224
341
|
export class InlinePicture {
|
|
225
342
|
constructor(
|
|
226
343
|
/** The `w:drawing` element pair. */
|
|
227
344
|
element,
|
|
228
345
|
/** The run holding the drawing, and the array holding the run. */
|
|
229
|
-
run, paragraph
|
|
346
|
+
run, paragraph,
|
|
347
|
+
/** The run's item the drawing stands in, where that is not the drawing: a kept `mc:AlternateContent`. */
|
|
348
|
+
holder) {
|
|
230
349
|
this.element = element;
|
|
231
350
|
this.run = run;
|
|
232
351
|
this.paragraph = paragraph;
|
|
352
|
+
this.holder = holder;
|
|
233
353
|
}
|
|
234
354
|
/** The wp:inline; a floating picture (wp:anchor) is not an InlinePicture, and throws. */
|
|
235
355
|
get inline() {
|
|
@@ -272,20 +392,39 @@ export class InlinePicture {
|
|
|
272
392
|
set altTextTitle(text) {
|
|
273
393
|
this.inline.docPr.title = text;
|
|
274
394
|
}
|
|
275
|
-
/**
|
|
395
|
+
/**
|
|
396
|
+
* The kind of image, from the content type of the part `getBase64` reads: `Svg` for an SVG picture
|
|
397
|
+
* (`svgImagePart`), else the `a:blip`'s part's. Office JS gives `null` for an SVG picture, though its
|
|
398
|
+
* typings declare the property non-null and `Word.ImageFormat` has `Svg` (test/README.md check 45);
|
|
399
|
+
* this package gives `Svg`, a departure (CR-002 section 52).
|
|
400
|
+
*/
|
|
276
401
|
get imageFormat() {
|
|
277
|
-
var _a;
|
|
278
|
-
const part = this.imagePart();
|
|
279
|
-
return (
|
|
402
|
+
var _a, _b;
|
|
403
|
+
const part = (_a = this.svgImagePart()) !== null && _a !== void 0 ? _a : this.imagePart();
|
|
404
|
+
return (_b = (part && FORMAT_BY_CONTENT_TYPE[part.contentType])) !== null && _b !== void 0 ? _b : 'Unsupported';
|
|
280
405
|
}
|
|
281
406
|
/** The relationship id of the embedded image (r:embed), if any (extension). */
|
|
282
407
|
get relId() {
|
|
283
408
|
var _a, _b;
|
|
284
409
|
return (_b = (_a = this.pic()) === null || _a === void 0 ? void 0 : _a.blipFill.blip) === null || _b === void 0 ? void 0 : _b.embed;
|
|
285
410
|
}
|
|
286
|
-
/**
|
|
411
|
+
/**
|
|
412
|
+
* The relationship id of the SVG the picture embeds beside its image, if any (extension): the
|
|
413
|
+
* `r:embed` of an `asvg:svgBlip` in the `a:blip`'s `a:extLst`, as Office 2016 and later write an SVG
|
|
414
|
+
* picture, with the `a:blip`'s own image a PNG of it for readers that don't understand SVG.
|
|
415
|
+
*/
|
|
416
|
+
get svgRelId() {
|
|
417
|
+
var _a, _b;
|
|
418
|
+
const embed = (_b = svgBlipOf((_a = this.pic()) === null || _a === void 0 ? void 0 : _a.blipFill.blip)) === null || _b === void 0 ? void 0 : _b.embed;
|
|
419
|
+
return embed === undefined || embed === '' ? undefined : embed;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* The image's bytes as base64 (Office JS getBase64ImageSrc). For an SVG picture, the SVG
|
|
423
|
+
* (`svgImagePart`), as Office JS gives it (test/README.md check 43); else the `a:blip`'s image.
|
|
424
|
+
*/
|
|
287
425
|
async getBase64() {
|
|
288
|
-
|
|
426
|
+
var _a;
|
|
427
|
+
const part = (_a = this.svgImagePart()) !== null && _a !== void 0 ? _a : this.imagePart();
|
|
289
428
|
if (!part)
|
|
290
429
|
throw new Docx4JException('This picture has no image part (a linked or missing image)');
|
|
291
430
|
return base64Encode(await part.getBytes());
|
|
@@ -294,29 +433,97 @@ export class InlinePicture {
|
|
|
294
433
|
getBase64ImageSrc() {
|
|
295
434
|
return this.getBase64();
|
|
296
435
|
}
|
|
297
|
-
/** The image part this picture embeds (extension). */
|
|
436
|
+
/** The image part this picture embeds: the `a:blip`'s, which is an SVG picture's PNG (extension). */
|
|
298
437
|
imagePart() {
|
|
438
|
+
return this.partFor(this.relId);
|
|
439
|
+
}
|
|
440
|
+
/** The SVG part an SVG picture embeds beside its image (`svgRelId`), if any (extension). */
|
|
441
|
+
svgImagePart() {
|
|
442
|
+
return this.partFor(this.svgRelId);
|
|
443
|
+
}
|
|
444
|
+
partFor(relId) {
|
|
299
445
|
var _a;
|
|
300
|
-
const relId = this.relId;
|
|
301
446
|
const source = this.paragraph.parentBody.part;
|
|
302
447
|
if (!relId || !source)
|
|
303
448
|
return undefined;
|
|
304
449
|
const part = (_a = source.relationshipsPart) === null || _a === void 0 ? void 0 : _a.getPart(relId);
|
|
305
450
|
return part instanceof ImagePart ? part : undefined;
|
|
306
451
|
}
|
|
307
|
-
/**
|
|
452
|
+
/**
|
|
453
|
+
* Removes the picture; the run goes too when the picture was all it held. A picture in a kept
|
|
454
|
+
* `mc:AlternateContent` takes the element with it, every branch (`holder`).
|
|
455
|
+
*
|
|
456
|
+
* While the package tracks changes it is marked deleted instead: its run goes into a `w:del`, and
|
|
457
|
+
* when the run held more than the picture, the picture is first given a run of its own with the
|
|
458
|
+
* same properties, so the deletion holds it alone. Accepting the deletion then takes it, and its
|
|
459
|
+
* image part with it (CR-002 section 46). A picture its author inserted under tracking is taken
|
|
460
|
+
* back, as `Paragraph.delete` takes back its own text, and one inside a deletion is refused.
|
|
461
|
+
*
|
|
462
|
+
* A picture removed outright (tracking off, or taken back) queues its image relationship on the
|
|
463
|
+
* package. On save the relationship goes if nothing then references it, and the image part with it
|
|
464
|
+
* when nothing else targets it. Waiting for the save lets a picture cut and pasted back keep its
|
|
465
|
+
* image.
|
|
466
|
+
*/
|
|
308
467
|
delete() {
|
|
309
|
-
var _a;
|
|
468
|
+
var _a, _b;
|
|
469
|
+
const place = placeOf(this.paragraph.p, this.run);
|
|
470
|
+
if (!place)
|
|
471
|
+
return;
|
|
472
|
+
const revision = place.revision;
|
|
473
|
+
const tracker = this.paragraph.changeTracker;
|
|
474
|
+
if (tracker && ((revision === null || revision === void 0 ? void 0 : revision.kind) === 'del' || (revision === null || revision === void 0 ? void 0 : revision.kind) === 'moveFrom')) {
|
|
475
|
+
throw new Docx4JException(`This picture is inside a w:${revision.kind} (deleted by ${revision.value.author}); reject the deletion first`);
|
|
476
|
+
}
|
|
477
|
+
if (tracker && !tracker.ownInsertion(revision)) {
|
|
478
|
+
this.markDeleted(tracker, place);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const item = (_a = this.holder) !== null && _a !== void 0 ? _a : this.element;
|
|
310
482
|
const content = this.run.value.content;
|
|
311
|
-
const at = (
|
|
483
|
+
const at = (_b = content === null || content === void 0 ? void 0 : content.indexOf(item)) !== null && _b !== void 0 ? _b : -1;
|
|
312
484
|
if (content && at >= 0)
|
|
313
485
|
content.splice(at, 1);
|
|
314
486
|
if (!content || content.length === 0) {
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
487
|
+
const i = place.items.indexOf(this.run);
|
|
488
|
+
if (i >= 0)
|
|
489
|
+
place.items.splice(i, 1);
|
|
490
|
+
if (revision && revision.items.length === 0) { // an insertion left empty goes with it
|
|
491
|
+
const j = revision.owner.indexOf(revision.element);
|
|
492
|
+
if (j >= 0)
|
|
493
|
+
revision.owner.splice(j, 1);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
const ids = new Set();
|
|
497
|
+
relationshipIdsIn(item, ids);
|
|
498
|
+
const pkg = this.paragraph.parentBody.package_;
|
|
499
|
+
queueRelationshipRemovals(pkg === null || pkg === void 0 ? void 0 : pkg.pendingRelationshipRemovals, this.paragraph.parentBody.part, ids);
|
|
500
|
+
}
|
|
501
|
+
/** The picture's run in a `w:del`, the picture split into a run of its own when the run held more. */
|
|
502
|
+
markDeleted(tracker, place) {
|
|
503
|
+
var _a, _b;
|
|
504
|
+
const run = this.run;
|
|
505
|
+
const at = place.items.indexOf(run);
|
|
506
|
+
const content = ((_a = run.value.content) !== null && _a !== void 0 ? _a : []);
|
|
507
|
+
const item = (_b = this.holder) !== null && _b !== void 0 ? _b : this.element;
|
|
508
|
+
const i = content.indexOf(item);
|
|
509
|
+
if (at < 0 || i < 0)
|
|
510
|
+
return;
|
|
511
|
+
const before = content.slice(0, i);
|
|
512
|
+
const after = content.slice(i + 1);
|
|
513
|
+
const pieces = [];
|
|
514
|
+
let alone = run;
|
|
515
|
+
if (before.length > 0 || after.length > 0) {
|
|
516
|
+
alone = runOf([item], copyRPr(run.value.rPr));
|
|
517
|
+
if (before.length > 0) {
|
|
518
|
+
run.value.content = before;
|
|
519
|
+
pieces.push(run);
|
|
520
|
+
}
|
|
319
521
|
}
|
|
522
|
+
pieces.push(tracker.del([alone]));
|
|
523
|
+
if (after.length > 0)
|
|
524
|
+
pieces.push(runOf(after, copyRPr(run.value.rPr)));
|
|
525
|
+
place.items.splice(at, 1, ...pieces);
|
|
526
|
+
linkParents(pieces, place.parent);
|
|
320
527
|
}
|
|
321
528
|
pic() {
|
|
322
529
|
const any = this.inline.graphic.graphicData.any;
|
|
@@ -338,24 +545,27 @@ export class InlinePicture {
|
|
|
338
545
|
}
|
|
339
546
|
}
|
|
340
547
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
var _a, _b, _c;
|
|
548
|
+
function placeOf(p, run) {
|
|
549
|
+
const seek = (items, parent, revision) => {
|
|
550
|
+
var _a, _b, _c, _d;
|
|
345
551
|
if (!items)
|
|
346
552
|
return undefined;
|
|
347
553
|
if (items.includes(run))
|
|
348
|
-
return items;
|
|
554
|
+
return revision ? { items, parent, revision } : { items, parent };
|
|
349
555
|
for (const item of items) {
|
|
350
556
|
const v = item.value;
|
|
351
557
|
if (typeof v !== 'object' || v === null)
|
|
352
558
|
continue;
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
559
|
+
const holder = (_a = v.sdtContent) !== null && _a !== void 0 ? _a : v;
|
|
560
|
+
const nested = (_c = (_b = v.content) !== null && _b !== void 0 ? _b : v.customXmlOrSmartTagOrSdt) !== null && _c !== void 0 ? _c : (_d = v.sdtContent) === null || _d === void 0 ? void 0 : _d.content;
|
|
561
|
+
const kind = revisionKindOf(item);
|
|
562
|
+
const inner = kind && nested ? { kind, element: item, value: v, items: nested, owner: items } : revision;
|
|
563
|
+
const found = seek(nested, holder, inner);
|
|
564
|
+
if (found)
|
|
565
|
+
return found;
|
|
356
566
|
}
|
|
357
567
|
return undefined;
|
|
358
568
|
};
|
|
359
|
-
return seek(p.content);
|
|
569
|
+
return seek(p.content, p, undefined);
|
|
360
570
|
}
|
|
361
571
|
export { IMAGE_CONTENT_TYPES_BY_EXTENSION };
|
|
@@ -5,7 +5,7 @@ import { Docx4JException } from '../../opc/exceptions.mjs';
|
|
|
5
5
|
import { DEFAULT_NUMBERING_XML } from '../../parts/wml/defaultNumbering.mjs';
|
|
6
6
|
import { NumberingState, storyKindOf } from '../listnumbering/state.mjs';
|
|
7
7
|
import { LEVELS } from '../listnumbering/definitions.mjs';
|
|
8
|
-
import { isElement, linkParents } from './tree.mjs';
|
|
8
|
+
import { isElement, linkParents, selectedBranchOf, ALTERNATE_CONTENT } from './tree.mjs';
|
|
9
9
|
import { Font } from './Font.mjs';
|
|
10
10
|
function packageOf(body) {
|
|
11
11
|
const pkg = body.package_;
|
|
@@ -60,6 +60,9 @@ function numRefOf(paragraph) {
|
|
|
60
60
|
* counts in. `test/parity.test.mjs`'s `walkNumbering`, which is the harness's own walk: a text
|
|
61
61
|
* box's paragraphs stay in this story's list (they are in the part, in document order) but
|
|
62
62
|
* count in a story of their own, which is docx4j's rule (CR-014 probe P7).
|
|
63
|
+
*
|
|
64
|
+
* A kept `mc:AlternateContent` is walked through its selected branch alone (`selectedBranchOf`,
|
|
65
|
+
* docx4j's `McMode.READ`), so a text box held in both branches counts once (CR-010 phase 3).
|
|
63
66
|
*/
|
|
64
67
|
function walkStory(root, state, emit) {
|
|
65
68
|
const walk = (value, current) => {
|
|
@@ -70,11 +73,17 @@ function walkStory(root, state, emit) {
|
|
|
70
73
|
}
|
|
71
74
|
if (typeof value !== 'object' || value === null)
|
|
72
75
|
return;
|
|
76
|
+
if (typeof value.nodeType === 'number')
|
|
77
|
+
return; // DOM the model does not type
|
|
73
78
|
if (isElement(value)) {
|
|
74
79
|
walk(value.value, current);
|
|
75
80
|
return;
|
|
76
81
|
}
|
|
77
82
|
const node = value;
|
|
83
|
+
if (node.TYPE_NAME === ALTERNATE_CONTENT) {
|
|
84
|
+
walk(selectedBranchOf(node), current);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
78
87
|
let inner = current;
|
|
79
88
|
if (node.TYPE_NAME === 'org_docx4j_wml.P')
|
|
80
89
|
emit(node, current);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
|
|
2
2
|
import type { CellContext } from '../properties/table.mjs';
|
|
3
|
-
import { type Element, W_NS, type TextSegment, type TextViewOptions } from './tree.mjs';
|
|
3
|
+
import { type Element, W_NS, type SpanEdges, type SegmentOptions, type TextSegment, type TextViewOptions } from './tree.mjs';
|
|
4
4
|
import { Font, type FontTracking } from './Font.mjs';
|
|
5
5
|
import { Range } from './Range.mjs';
|
|
6
6
|
import { type SearchOptions } from './search.mjs';
|
|
@@ -160,7 +160,12 @@ export declare class Paragraph {
|
|
|
160
160
|
get parentTableCell(): TableCell | undefined;
|
|
161
161
|
/** The run-level content controls in this paragraph, in order, nested ones included. */
|
|
162
162
|
get contentControls(): ContentControl[];
|
|
163
|
-
/**
|
|
163
|
+
/**
|
|
164
|
+
* The inline pictures in this paragraph, in order (Office JS Paragraph.inlinePictures): each
|
|
165
|
+
* `w:drawing` whose `wp:inline` holds a `pic:pic`, in a run or in the selected branch of a kept
|
|
166
|
+
* `mc:AlternateContent` (`picturesIn`). An inline chart, text box or group is not one (CR-010
|
|
167
|
+
* section 12.6).
|
|
168
|
+
*/
|
|
164
169
|
get inlinePictures(): InlinePicture[];
|
|
165
170
|
/** Text inserted at the start, the end, or replacing the whole paragraph's text. */
|
|
166
171
|
insertText(text: string, location: 'Start' | 'End' | 'Replace'): Range;
|
|
@@ -198,6 +203,10 @@ export declare class Paragraph {
|
|
|
198
203
|
replaceText(find: string, replace: string, options?: SearchOptions): number;
|
|
199
204
|
/** The tracked changes in this paragraph, in document order (CR-002 phase F). */
|
|
200
205
|
getTrackedChanges(): TrackedChange[];
|
|
206
|
+
/**
|
|
207
|
+
* The paragraph's range: 'Whole' and 'Content' hold its whole content, the pictures at its start and
|
|
208
|
+
* end among them, so deleting it deletes them (CR-002 section 48); 'Start' and 'End' are collapsed.
|
|
209
|
+
*/
|
|
201
210
|
getRange(location?: 'Whole' | 'Start' | 'End' | 'Content'): Range;
|
|
202
211
|
/**
|
|
203
212
|
* Removes the paragraph from its container. While the package tracks changes, the content
|
|
@@ -214,6 +223,12 @@ export declare class Paragraph {
|
|
|
214
223
|
* Does nothing when the mark is already deleted.
|
|
215
224
|
*/
|
|
216
225
|
markDeletedInPlace(tracker: ChangeTracker): void;
|
|
226
|
+
/**
|
|
227
|
+
* Queues the relationships that content removed outright referenced (a picture's image, a link's
|
|
228
|
+
* target), to be settled when the package is saved: each goes then if nothing references it, and
|
|
229
|
+
* its part with it when nothing else targets that (CR-002 sections 47 and 48).
|
|
230
|
+
*/
|
|
231
|
+
releaseRelationships(removed: unknown): void;
|
|
217
232
|
/** The paragraph as XML (extension; Office JS getOoxml wraps it in a package). */
|
|
218
233
|
getXml(): Promise<string>;
|
|
219
234
|
/** docx4j: the P's content list. */
|
|
@@ -253,15 +268,19 @@ export declare class Paragraph {
|
|
|
253
268
|
get changeTracker(): ChangeTracker | undefined;
|
|
254
269
|
/** What `Font` needs to write `w:rPrChange`: the tracker, and the runs that are our own insertion (`Range` shares it). */
|
|
255
270
|
fontTracking(): FontTracking | undefined;
|
|
256
|
-
/** The text segments (w:t, w:tab, ...) with their offsets. */
|
|
257
|
-
segments(options?:
|
|
271
|
+
/** The text segments (w:t, w:tab, ...) with their offsets; `{ objects: true }` adds the pictures. */
|
|
272
|
+
segments(options?: SegmentOptions): TextSegment[];
|
|
258
273
|
/**
|
|
259
274
|
* Replaces the text in [start, end) with `text` and returns the range of the inserted text.
|
|
260
275
|
* Runs are edited in place: a w:t loses or gains characters, a tab or break inside the range
|
|
261
276
|
* is removed, a run left empty is removed. Text is inserted into the w:t at `start`,
|
|
262
277
|
* else into a new run with the formatting of the nearest run.
|
|
278
|
+
*
|
|
279
|
+
* The pictures in the span go with its text: those strictly inside it, and those at an edge `edges`
|
|
280
|
+
* says the span holds (`WHOLE` for a paragraph's whole content), so a span of no text can still
|
|
281
|
+
* delete a picture (CR-002 section 48).
|
|
263
282
|
*/
|
|
264
|
-
splice(start: number, end: number, text: string, prefer?: 'before' | 'after'): Range;
|
|
283
|
+
splice(start: number, end: number, text: string, prefer?: 'before' | 'after', edges?: SpanEdges): Range;
|
|
265
284
|
/**
|
|
266
285
|
* Inserts run-level items (runs, hyperlinks, content controls) at a text offset, splitting the
|
|
267
286
|
* run there when the offset falls inside one. PARENT is linked on what is inserted.
|
|
@@ -317,6 +336,8 @@ export declare class Paragraph {
|
|
|
317
336
|
* The text in [start, end) becomes a deletion: the runs holding it are isolated, their `w:t`
|
|
318
337
|
* turned into `w:delText`, and each run of consecutive ones moved into one `w:del`. Text this
|
|
319
338
|
* author had inserted is simply removed, as Word does, rather than nested in a `w:del`.
|
|
339
|
+
* The pictures in the span (`splice`'s `edges`) are deleted with the text, each in a run of its own
|
|
340
|
+
* where its run held more, as `InlinePicture.delete` gives it one (CR-002 section 48).
|
|
320
341
|
* Returns the `w:del` elements it made, in document order.
|
|
321
342
|
*/
|
|
322
343
|
private deleteText;
|
|
@@ -365,6 +386,8 @@ export declare class Paragraph {
|
|
|
365
386
|
* Splits runs so that every run holding text in [start, end) holds nothing else, and returns
|
|
366
387
|
* the segments inside the span. `splitAt` does the `w:t` boundaries; this also moves the items
|
|
367
388
|
* of a run that straddles a boundary (a tab, a break, a drawing) out into runs of their own.
|
|
389
|
+
* The pictures in the span (`splice`'s `edges`) are among the segments returned, and a run
|
|
390
|
+
* holding one holds nothing outside the span either.
|
|
368
391
|
*/
|
|
369
392
|
private isolate;
|
|
370
393
|
/** Moves the items before `seg` into a run of their own, in front of `seg`'s run. */
|