@docx4j/core-ts 0.4.0 → 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 +5 -2
- package/dist/html/convert.mjs +38 -14
- package/dist/model/content/Body.d.mts +13 -1
- package/dist/model/content/Body.mjs +90 -19
- package/dist/model/content/Comment.mjs +7 -0
- package/dist/model/content/InlinePicture.d.mts +71 -7
- package/dist/model/content/InlinePicture.mjs +201 -28
- 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/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 +2 -2
- package/dist/model/content/index.mjs +1 -1
- package/dist/model/content/relationships.d.mts +30 -0
- package/dist/model/content/relationships.mjs +133 -0
- package/dist/model/content/tree.d.mts +52 -2
- package/dist/model/content/tree.mjs +48 -3
- package/dist/model/customxml/pictures.d.mts +5 -2
- package/dist/model/customxml/pictures.mjs +13 -7
- 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 +98 -2
- package/dist/parts/XmlPart.d.mts +26 -5
- package/dist/parts/XmlPart.mjs +67 -18
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { deepCopy } from '@docx4j/generated-objects-ts';
|
|
2
|
-
import { Docx4JException } from '../../opc/exceptions.mjs';
|
|
2
|
+
import { Docx4JException, PartUnmarshalException } from '../../opc/exceptions.mjs';
|
|
3
3
|
import { log } from './log.mjs';
|
|
4
4
|
import { NumberingDefinitions } from '../listnumbering/definitions.mjs';
|
|
5
5
|
import { applyPPr, applyPPrBase, applyRPr, applyStyle, applyStyleLevel, applyTblPr, applyToggles, hasDirectFormattingPPr, hasDirectFormattingRPr, isCyclic, isEmptyStyle, } from './styleUtil.mjs';
|
|
@@ -75,6 +75,26 @@ export function headingLevelByName(style) {
|
|
|
75
75
|
const match = HEADING_NAME.exec(name.trim());
|
|
76
76
|
return match ? Number(match[1]) : -1;
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* A styles or numbering part the resolver can't do without: when it can't be read, nothing in the
|
|
80
|
+
* document can be given its formatting, so the document is refused (Jason, 2026-10-17), and the
|
|
81
|
+
* error says so before it says what was wrong where. A `PartUnmarshalException` stays one, with its
|
|
82
|
+
* part, location, line and column, so a caller showing the fault reads the same fields as for any
|
|
83
|
+
* part.
|
|
84
|
+
*/
|
|
85
|
+
async function readOrRefuse(part, role) {
|
|
86
|
+
try {
|
|
87
|
+
return await part.readContents();
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
const why = role === 'styles' ? "every paragraph's formatting depends on it" : "its lists' numbers and indents depend on it";
|
|
91
|
+
const lead = `The document cannot be opened: its ${role} part could not be read, and ${why}.`;
|
|
92
|
+
if (e instanceof PartUnmarshalException) {
|
|
93
|
+
throw new PartUnmarshalException(`${lead} ${e.message}`, e.partName, { cause: e, location: e.location, line: e.line, column: e.column });
|
|
94
|
+
}
|
|
95
|
+
throw new Docx4JException(`${lead} ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
78
98
|
export class PropertyResolver {
|
|
79
99
|
constructor(provider,
|
|
80
100
|
/** Set when the styles tree the resolver holds is not the styles part's own. */
|
|
@@ -104,6 +124,12 @@ export class PropertyResolver {
|
|
|
104
124
|
this.composedRPrCache = new CompositionCache();
|
|
105
125
|
/** See {@link appliesTableStyleSizeJcException}; read once per `refresh()`. */
|
|
106
126
|
this.tableStyleSizeJcException = true;
|
|
127
|
+
/**
|
|
128
|
+
* Whether the document's own `w:docDefaults` state a size, rather than the 10pt the resolver
|
|
129
|
+
* makes explicit where they do not: the size exception takes the document defaults' size only
|
|
130
|
+
* where they state one. Read once per `refresh()`.
|
|
131
|
+
*/
|
|
132
|
+
this.docDefaultsStateSize = false;
|
|
107
133
|
/** Missing styles are logged once each per resolver. */
|
|
108
134
|
this.missingLogged = new Set();
|
|
109
135
|
this.source = provider();
|
|
@@ -132,8 +158,8 @@ export class PropertyResolver {
|
|
|
132
158
|
}
|
|
133
159
|
const stylesPart = main.styleDefinitionsPart;
|
|
134
160
|
const numberingPart = main.numberingDefinitionsPart;
|
|
135
|
-
const privateStyles = stylesPart === undefined ? undefined : await stylesPart
|
|
136
|
-
const privateNumbering = numberingPart === undefined ? undefined : await numberingPart
|
|
161
|
+
const privateStyles = stylesPart === undefined ? undefined : await readOrRefuse(stylesPart, 'styles');
|
|
162
|
+
const privateNumbering = numberingPart === undefined ? undefined : await readOrRefuse(numberingPart, 'numbering');
|
|
137
163
|
// the settings are read for two compatibility values (CR-007), privately, as the others are
|
|
138
164
|
const settingsPart = main.documentSettingsPart;
|
|
139
165
|
let privateSettings;
|
|
@@ -189,6 +215,7 @@ export class PropertyResolver {
|
|
|
189
215
|
? { TYPE_NAME: 'org_docx4j_wml.PPr' } : deepCopy(docDefaults.pPrDefault.pPr);
|
|
190
216
|
this.documentDefaultRPr = ((_f = docDefaults === null || docDefaults === void 0 ? void 0 : docDefaults.rPrDefault) === null || _f === void 0 ? void 0 : _f.rPr) === undefined
|
|
191
217
|
? { TYPE_NAME: 'org_docx4j_wml.RPr' } : deepCopy(docDefaults.rPrDefault.rPr);
|
|
218
|
+
this.docDefaultsStateSize = this.documentDefaultRPr.sz !== undefined;
|
|
192
219
|
if (this.documentDefaultRPr.sz === undefined) {
|
|
193
220
|
// Word's default made explicit: 10pt where nothing states a size (measured, CR-015 probe
|
|
194
221
|
// styles-no-size-anywhere: identical to an explicit 10pt run)
|
|
@@ -384,7 +411,7 @@ export class PropertyResolver {
|
|
|
384
411
|
return styleId;
|
|
385
412
|
}
|
|
386
413
|
getEffectivePPr(arg, cellContext) {
|
|
387
|
-
if (cellContext
|
|
414
|
+
if (this.composes(cellContext) && typeof arg !== 'string') {
|
|
388
415
|
const composed = this.composedPPr(this.paragraphStyleOf(arg), cellContext);
|
|
389
416
|
if (!hasDirectFormattingPPr(arg))
|
|
390
417
|
return composed;
|
|
@@ -415,7 +442,7 @@ export class PropertyResolver {
|
|
|
415
442
|
getEffectiveRPr(arg, pPr, cellContext) {
|
|
416
443
|
if (typeof arg === 'string')
|
|
417
444
|
return this.effectiveRPrOfStyle(arg);
|
|
418
|
-
if (cellContext
|
|
445
|
+
if (this.composes(cellContext)) {
|
|
419
446
|
const inTable = deepCopy(this.composedRPr(this.paragraphStyleOf(pPr), cellContext));
|
|
420
447
|
this.applyCharacterStyleAndDirect(arg, inTable);
|
|
421
448
|
return inTable;
|
|
@@ -462,23 +489,43 @@ export class PropertyResolver {
|
|
|
462
489
|
}
|
|
463
490
|
/**
|
|
464
491
|
* The run properties of the paragraph mark: document defaults, the paragraph style's run
|
|
465
|
-
* properties, then the `w:pPr`'s own `w:rPr`. What sizes an empty
|
|
466
|
-
* label starts from.
|
|
492
|
+
* properties, the mark's character style, then the `w:pPr`'s own `w:rPr`. What sizes an empty
|
|
493
|
+
* paragraph, and what a list label starts from.
|
|
467
494
|
*/
|
|
468
495
|
getEffectiveParagraphMarkRPr(pPr, cellContext) {
|
|
469
|
-
if (cellContext
|
|
496
|
+
if (this.composes(cellContext)) {
|
|
470
497
|
// in a table cell: the table level beneath the paragraph style's (CR-007)
|
|
471
498
|
const inTable = deepCopy(this.composedRPr(this.paragraphStyleOf(pPr), cellContext));
|
|
472
499
|
if ((pPr === null || pPr === void 0 ? void 0 : pPr.rPr) !== undefined)
|
|
473
|
-
|
|
500
|
+
this.applyMarkRPr(pPr.rPr, inTable);
|
|
474
501
|
return inTable;
|
|
475
502
|
}
|
|
476
503
|
const effective = deepCopy(this.documentDefaultRPr);
|
|
477
504
|
applyRPr(this.getChainRPr(this.paragraphStyleOf(pPr)), effective);
|
|
478
505
|
if ((pPr === null || pPr === void 0 ? void 0 : pPr.rPr) !== undefined)
|
|
479
|
-
|
|
506
|
+
this.applyMarkRPr(pPr.rPr, effective);
|
|
480
507
|
return effective;
|
|
481
508
|
}
|
|
509
|
+
/**
|
|
510
|
+
* The paragraph mark's character style (`w:pPr/w:rPr/w:rStyle`) as a level over what is
|
|
511
|
+
* beneath it, then the mark's direct formatting: the order {@link applyCharacterStyleAndDirect}
|
|
512
|
+
* gives a run. docx4j `applyMarkRPr`, since 17.3.2 (fb2a764f7: until then the mark's
|
|
513
|
+
* `w:rStyle` was copied as an attribute and never resolved, so an empty paragraph whose mark
|
|
514
|
+
* names a 9pt character style was sized at its paragraph style's 12pt).
|
|
515
|
+
*/
|
|
516
|
+
applyMarkRPr(mark, effective) {
|
|
517
|
+
var _a;
|
|
518
|
+
const runStyleId = (_a = mark.rStyle) === null || _a === void 0 ? void 0 : _a.val;
|
|
519
|
+
if (runStyleId !== undefined) {
|
|
520
|
+
if (this.getLiveStyle(runStyleId) === undefined) {
|
|
521
|
+
this.logMissing(runStyleId);
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
applyStyleLevel(this.getChainRPr(runStyleId), effective, this.documentDefaultRPr);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
applyRPr(mark, effective);
|
|
528
|
+
}
|
|
482
529
|
/** Whether the run states any formatting of its own (docx4j's deprecated `hasDirectRPrFormatting`). */
|
|
483
530
|
hasDirectRPrFormatting(rPr) {
|
|
484
531
|
return hasDirectFormattingRPr(rPr);
|
|
@@ -609,6 +656,15 @@ export class PropertyResolver {
|
|
|
609
656
|
const found = enclosingCellOf(p);
|
|
610
657
|
return found === undefined ? undefined : this.tableContext(found.tbl).forParagraph(found.tr, found.tc, p.pPr);
|
|
611
658
|
}
|
|
659
|
+
/**
|
|
660
|
+
* Whether a paragraph in this context is composed over its table level: where the table style
|
|
661
|
+
* gives it text formatting, or, under [MS-DOCX]'s size exception, where the style formats text
|
|
662
|
+
* anywhere ({@link CellContext.styleFormatsText}). docx4j `composes` (900b6a212, 17.3.2).
|
|
663
|
+
*/
|
|
664
|
+
composes(cellContext) {
|
|
665
|
+
return cellContext !== undefined && (cellContext.formatsText
|
|
666
|
+
|| (this.tableStyleSizeJcException && cellContext.styleFormatsText));
|
|
667
|
+
}
|
|
612
668
|
clearTableCaches() {
|
|
613
669
|
this.tableStyleChains.clear();
|
|
614
670
|
this.composedPPrCache.clear();
|
|
@@ -663,18 +719,29 @@ export class PropertyResolver {
|
|
|
663
719
|
// the table and the paragraph style are two levels of the hierarchy: the twelve toggles
|
|
664
720
|
// combine across them rather than the paragraph's overriding (17.7.3)
|
|
665
721
|
applyToggles(paragraphLevel, tableLevel, this.documentDefaultRPr, composed);
|
|
666
|
-
|
|
722
|
+
// The size the table level gives is its own where the table style states one, else the
|
|
723
|
+
// document defaults' where they state one: Word applies the exception wherever the table
|
|
724
|
+
// style formats text at all, not only where it states a size (docx4j 900b6a212, measured on
|
|
725
|
+
// its table-style-size-trigger probes: in mode 12, with docDefaults 11pt and Normal 12pt, a
|
|
726
|
+
// table style with only a w:pPr, only an rPr colour, or only a firstRow format sets its
|
|
727
|
+
// table's paragraphs at 11pt; with none of these, at Normal's 12pt). But where neither the
|
|
728
|
+
// table style nor the docDefaults state a size, Normal keeps its 12pt, not the 10pt Word
|
|
729
|
+
// falls back to (docx4j's corpus documents 10244 and 12723).
|
|
730
|
+
const tableStatesSize = (tableOnly === null || tableOnly === void 0 ? void 0 : tableOnly.sz) !== undefined;
|
|
731
|
+
if (this.tableStyleSizeJcException && ctx.styleFormatsText && tableLevel.sz !== undefined
|
|
732
|
+
&& (tableStatesSize || this.docDefaultsStateSize)
|
|
667
733
|
&& this.paragraphStyleGivesWay(styleId, true)) {
|
|
668
|
-
composed.sz = deepCopy(
|
|
734
|
+
composed.sz = deepCopy(tableLevel.sz);
|
|
669
735
|
}
|
|
670
736
|
this.composedRPrCache.set(styleId, ctx.key, composed);
|
|
671
737
|
return composed;
|
|
672
738
|
}
|
|
673
739
|
/**
|
|
674
|
-
* [MS-DOCX]'s exception, for a paragraph in a table whose style
|
|
675
|
-
*
|
|
676
|
-
* style
|
|
677
|
-
* the paragraph's own which
|
|
740
|
+
* [MS-DOCX]'s exception, for a paragraph in a table whose style formats text (for the size: the
|
|
741
|
+
* style need not state one, since docx4j 17.3.2) or states a justification: the paragraph's
|
|
742
|
+
* style gives way to it if the style is the default paragraph style, or states no size
|
|
743
|
+
* (justification) of its own, and resolves to 12pt (left). A style of the paragraph's own which
|
|
744
|
+
* states one keeps it.
|
|
678
745
|
*/
|
|
679
746
|
paragraphStyleGivesWay(styleId, size) {
|
|
680
747
|
var _a, _b, _c, _d;
|
|
@@ -39,7 +39,12 @@ export declare function isTrue(value: wml.BooleanDefaultTrue | undefined): boole
|
|
|
39
39
|
export declare function isEmptyRStyle(value: wml.RStyle | undefined): boolean;
|
|
40
40
|
/** docx4j `isEmpty(RFonts)`: insensitive to a `w:hint` (which `apply` still carries). */
|
|
41
41
|
export declare function isEmptyRFonts(value: wml.RFonts | undefined): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* A `w:color` stating only a theme colour is not empty: it is inherited, where until docx4j CR-034
|
|
44
|
+
* (2e381ef26) it was dropped.
|
|
45
|
+
*/
|
|
42
46
|
export declare function isEmptyColor(value: wml.Color | undefined): boolean;
|
|
47
|
+
/** The underline's colour attributes count, the theme ones too (docx4j CR-034, 2e381ef26). */
|
|
43
48
|
export declare function isEmptyU(value: wml.U | undefined): boolean;
|
|
44
49
|
export declare function isEmptyHighlight(value: wml.Highlight | undefined): boolean;
|
|
45
50
|
/** @since CR-015 phase 1: a style stating only `w:lang` used to count as empty and was never applied. */
|
|
@@ -87,8 +92,17 @@ export declare const mergePStyle: Merge<wml.PPrBase.PStyle>;
|
|
|
87
92
|
* formatted).
|
|
88
93
|
*/
|
|
89
94
|
export declare const mergeRFonts: Merge<wml.RFonts>;
|
|
95
|
+
/**
|
|
96
|
+
* A colour inherits as one (docx4j CR-034, 2e381ef26): where the source states a value or a theme
|
|
97
|
+
* reference, its value, theme colour, tint and shade replace the destination's together. Word
|
|
98
|
+
* draws a theme reference over the value written beside it, so taken one by one, a run's
|
|
99
|
+
* `w:val="FF0000"` under a style's `w:themeColor="accent1"` would keep the style's reference and
|
|
100
|
+
* draw accent1, where Word draws red. A tint or shade stated alone still inherits on its own. The
|
|
101
|
+
* same holds for the colours of `w:u`, `w:shd` (its pattern colour and its fill) and the borders.
|
|
102
|
+
* (Attribute by attribute until then, CR-015 phase 1.)
|
|
103
|
+
*/
|
|
90
104
|
export declare const mergeColor: Merge<wml.Color>;
|
|
91
|
-
/** `w:val` and the colour
|
|
105
|
+
/** `w:val` and the colour inherit on their own (CR-015 phase 1), the colour as one ({@link mergeColor}). */
|
|
92
106
|
export declare const mergeU: Merge<wml.U>;
|
|
93
107
|
/** `w:val`, `w:eastAsia` and `w:bidi` each name the language of one script range and each inherits. */
|
|
94
108
|
export declare const mergeLang: Merge<wml.CTLanguage>;
|
|
@@ -29,9 +29,12 @@ function emptyThemeColor(value) {
|
|
|
29
29
|
function attr(source, destination) {
|
|
30
30
|
return source === undefined ? destination : source;
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
/**
|
|
33
|
+
* docx4j `StyleUtil.statesColour`: a colour's value or theme reference is stated, so the colour
|
|
34
|
+
* inherits as one (docx4j CR-034, 2e381ef26; see {@link mergeColor}).
|
|
35
|
+
*/
|
|
36
|
+
function statesColour(val, theme) {
|
|
37
|
+
return !emptyString(val) || !emptyThemeColor(theme);
|
|
35
38
|
}
|
|
36
39
|
/** A merge with no per-attribute rule: a copy of the source where it has one, else the inherited. */
|
|
37
40
|
export function mergeReplace(source, destination) {
|
|
@@ -71,11 +74,20 @@ export function isEmptyRFonts(value) {
|
|
|
71
74
|
&& emptyString(value.eastAsia) && value.eastAsiaTheme === undefined
|
|
72
75
|
&& emptyString(value.hAnsi) && value.hAnsiTheme === undefined);
|
|
73
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* A `w:color` stating only a theme colour is not empty: it is inherited, where until docx4j CR-034
|
|
79
|
+
* (2e381ef26) it was dropped.
|
|
80
|
+
*/
|
|
74
81
|
export function isEmptyColor(value) {
|
|
75
|
-
return value === undefined
|
|
82
|
+
return value === undefined
|
|
83
|
+
|| (emptyString(value.val) && value.themeColor === undefined
|
|
84
|
+
&& emptyString(value.themeTint) && emptyString(value.themeShade));
|
|
76
85
|
}
|
|
86
|
+
/** The underline's colour attributes count, the theme ones too (docx4j CR-034, 2e381ef26). */
|
|
77
87
|
export function isEmptyU(value) {
|
|
78
|
-
return value === undefined
|
|
88
|
+
return value === undefined
|
|
89
|
+
|| (value.val === undefined && emptyString(value.color) && value.themeColor === undefined
|
|
90
|
+
&& emptyString(value.themeTint) && emptyString(value.themeShade));
|
|
79
91
|
}
|
|
80
92
|
export function isEmptyHighlight(value) {
|
|
81
93
|
return value === undefined || emptyString(value.val);
|
|
@@ -291,28 +303,49 @@ export const mergeRFonts = (source, destination) => {
|
|
|
291
303
|
out.hint = source.hint;
|
|
292
304
|
return out;
|
|
293
305
|
};
|
|
306
|
+
/**
|
|
307
|
+
* A colour inherits as one (docx4j CR-034, 2e381ef26): where the source states a value or a theme
|
|
308
|
+
* reference, its value, theme colour, tint and shade replace the destination's together. Word
|
|
309
|
+
* draws a theme reference over the value written beside it, so taken one by one, a run's
|
|
310
|
+
* `w:val="FF0000"` under a style's `w:themeColor="accent1"` would keep the style's reference and
|
|
311
|
+
* draw accent1, where Word draws red. A tint or shade stated alone still inherits on its own. The
|
|
312
|
+
* same holds for the colours of `w:u`, `w:shd` (its pattern colour and its fill) and the borders.
|
|
313
|
+
* (Attribute by attribute until then, CR-015 phase 1.)
|
|
314
|
+
*/
|
|
294
315
|
export const mergeColor = (source, destination) => {
|
|
295
316
|
if (isEmptyColor(source))
|
|
296
317
|
return destination;
|
|
297
318
|
const s = source;
|
|
298
319
|
const out = (destination !== null && destination !== void 0 ? destination : {});
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
320
|
+
if (statesColour(s.val, s.themeColor)) {
|
|
321
|
+
out.val = s.val;
|
|
322
|
+
out.themeColor = s.themeColor;
|
|
323
|
+
out.themeTint = s.themeTint;
|
|
324
|
+
out.themeShade = s.themeShade;
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
out.themeShade = attr(s.themeShade, out.themeShade);
|
|
328
|
+
out.themeTint = attr(s.themeTint, out.themeTint);
|
|
329
|
+
}
|
|
303
330
|
return out;
|
|
304
331
|
};
|
|
305
|
-
/** `w:val` and the colour
|
|
332
|
+
/** `w:val` and the colour inherit on their own (CR-015 phase 1), the colour as one ({@link mergeColor}). */
|
|
306
333
|
export const mergeU = (source, destination) => {
|
|
307
334
|
if (isEmptyU(source))
|
|
308
335
|
return destination;
|
|
309
336
|
const s = source;
|
|
310
337
|
const out = destination !== null && destination !== void 0 ? destination : {};
|
|
311
338
|
out.val = attr(s.val, out.val);
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
339
|
+
if (statesColour(s.color, s.themeColor)) {
|
|
340
|
+
out.color = s.color;
|
|
341
|
+
out.themeColor = s.themeColor;
|
|
342
|
+
out.themeTint = s.themeTint;
|
|
343
|
+
out.themeShade = s.themeShade;
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
out.themeTint = attr(s.themeTint, out.themeTint);
|
|
347
|
+
out.themeShade = attr(s.themeShade, out.themeShade);
|
|
348
|
+
}
|
|
316
349
|
return out;
|
|
317
350
|
};
|
|
318
351
|
/** `w:val`, `w:eastAsia` and `w:bidi` each name the language of one script range and each inherits. */
|
|
@@ -331,16 +364,29 @@ export const mergeShd = (source, destination) => {
|
|
|
331
364
|
return destination;
|
|
332
365
|
const s = source;
|
|
333
366
|
const out = (destination !== null && destination !== void 0 ? destination : {});
|
|
334
|
-
out.color = attr(s.color, out.color);
|
|
335
|
-
out.fill = attr(s.fill, out.fill);
|
|
336
367
|
out.val = attr(s.val, out.val);
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
368
|
+
// the pattern colour and the fill each inherit as one (see mergeColor), so a direct w:shd
|
|
369
|
+
// stating no fill still leaves an inherited w:themeFill (CR-015)
|
|
370
|
+
if (statesColour(s.color, s.themeColor)) {
|
|
371
|
+
out.color = s.color;
|
|
372
|
+
out.themeColor = s.themeColor;
|
|
373
|
+
out.themeTint = s.themeTint;
|
|
374
|
+
out.themeShade = s.themeShade;
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
out.themeTint = attr(s.themeTint, out.themeTint);
|
|
378
|
+
out.themeShade = attr(s.themeShade, out.themeShade);
|
|
379
|
+
}
|
|
380
|
+
if (statesColour(s.fill, s.themeFill)) {
|
|
381
|
+
out.fill = s.fill;
|
|
382
|
+
out.themeFill = s.themeFill;
|
|
383
|
+
out.themeFillTint = s.themeFillTint;
|
|
384
|
+
out.themeFillShade = s.themeFillShade;
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
out.themeFillTint = attr(s.themeFillTint, out.themeFillTint);
|
|
388
|
+
out.themeFillShade = attr(s.themeFillShade, out.themeFillShade);
|
|
389
|
+
}
|
|
344
390
|
return out;
|
|
345
391
|
};
|
|
346
392
|
export const mergeBorder = (source, destination) => {
|
|
@@ -348,13 +394,20 @@ export const mergeBorder = (source, destination) => {
|
|
|
348
394
|
return destination;
|
|
349
395
|
const s = source;
|
|
350
396
|
const out = (destination !== null && destination !== void 0 ? destination : {});
|
|
351
|
-
out.color = attr(s.color, out.color);
|
|
352
397
|
out.space = attr(s.space, out.space);
|
|
353
398
|
out.sz = attr(s.sz, out.sz);
|
|
354
|
-
out.themeColor = themeColor(s.themeColor, out.themeColor);
|
|
355
|
-
out.themeShade = attr(s.themeShade, out.themeShade);
|
|
356
|
-
out.themeTint = attr(s.themeTint, out.themeTint);
|
|
357
399
|
out.val = attr(s.val, out.val);
|
|
400
|
+
// the colour inherits as one (see mergeColor)
|
|
401
|
+
if (statesColour(s.color, s.themeColor)) {
|
|
402
|
+
out.color = s.color;
|
|
403
|
+
out.themeColor = s.themeColor;
|
|
404
|
+
out.themeTint = s.themeTint;
|
|
405
|
+
out.themeShade = s.themeShade;
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
out.themeShade = attr(s.themeShade, out.themeShade);
|
|
409
|
+
out.themeTint = attr(s.themeTint, out.themeTint);
|
|
410
|
+
}
|
|
358
411
|
return out;
|
|
359
412
|
};
|
|
360
413
|
export const mergePBdr = (source, destination) => {
|
|
@@ -40,9 +40,21 @@ export declare class CellContext {
|
|
|
40
40
|
/**
|
|
41
41
|
* Whether the table style gives this paragraph anything at all: its own `w:pPr` or `w:rPr`, or
|
|
42
42
|
* a conditional format with one. Where it does not, resolving with this context gives the same
|
|
43
|
-
* answer as resolving without one
|
|
43
|
+
* answer as resolving without one - except under [MS-DOCX]'s size exception, for which see
|
|
44
|
+
* {@link styleFormatsText}.
|
|
44
45
|
*/
|
|
45
46
|
readonly formatsText: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Whether the table style formats text anywhere: its own `w:pPr` or `w:rPr`, or a conditional
|
|
49
|
+
* format with one, whether or not that condition holds for this paragraph. Below compatibility
|
|
50
|
+
* mode 15 this, not what applies here, is what brings [MS-DOCX]'s size exception into play:
|
|
51
|
+
* measured on docx4j's table-style-size-trigger probes (its corpus document 1912), a table
|
|
52
|
+
* style with only a `w:pPr`, only an `w:rPr` colour, or only a `firstRow` format sets every row
|
|
53
|
+
* of its table at the document defaults' size where Normal states 12pt - the `firstRow`
|
|
54
|
+
* format's second row included - while a style with none of these leaves Normal's 12pt.
|
|
55
|
+
* docx4j `styleFormatsText()` (900b6a212, 17.3.2).
|
|
56
|
+
*/
|
|
57
|
+
readonly styleFormatsText: boolean;
|
|
46
58
|
/** What the table level's composition depends on. A value, for keying caches. */
|
|
47
59
|
readonly key: CellContextKey;
|
|
48
60
|
constructor(
|
|
@@ -138,7 +150,9 @@ export declare class TableContextTracker {
|
|
|
138
150
|
/**
|
|
139
151
|
* Walks everything under `root` (an object, an element pair or an array of them), in document
|
|
140
152
|
* order, calling `visit` for each paragraph with its context. It descends through every
|
|
141
|
-
* member but the `PARENT` pointers, so a text box's paragraphs are reached, in no table.
|
|
153
|
+
* member but the `PARENT` pointers, so a text box's paragraphs are reached, in no table. A kept
|
|
154
|
+
* `mc:AlternateContent` is descended through its selected branch alone (`selectedBranchOf`,
|
|
155
|
+
* docx4j's `McMode.READ`), so a text box held in both branches is visited once (CR-010 phase 3).
|
|
142
156
|
*/
|
|
143
157
|
walk(root: unknown, visit: (p: wml.P, cellContext: CellContext | undefined) => void): void;
|
|
144
158
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isEmptyPPr, isEmptyRPr } from './styleUtil.mjs';
|
|
2
|
-
import { rowsOf, cellsOf } from '../content/tree.mjs';
|
|
2
|
+
import { rowsOf, cellsOf, selectedBranchOf, ALTERNATE_CONTENT } from '../content/tree.mjs';
|
|
3
3
|
import { PRECEDENCE, DEFAULT_LOOK, lookOf, rowBandSize, colBandSize, rowCnf, resolve, rowConditions, applicable, formatsText, key as conditionsKey, } from './tableStyleConditions.mjs';
|
|
4
4
|
/** The conditions as bits over {@link PRECEDENCE}: an exact value for keying caches. */
|
|
5
5
|
function maskOf(conditions) {
|
|
@@ -43,6 +43,7 @@ export class CellContext {
|
|
|
43
43
|
tableStyleId,
|
|
44
44
|
/** That style's `w:basedOn` chain merged (`getTableStyleChain`): shared, read it only. */
|
|
45
45
|
tableStyle, conditions) {
|
|
46
|
+
var _a;
|
|
46
47
|
this.tableStyleId = tableStyleId;
|
|
47
48
|
this.tableStyle = tableStyle;
|
|
48
49
|
const c = new Set(conditions !== null && conditions !== void 0 ? conditions : []);
|
|
@@ -56,7 +57,10 @@ export class CellContext {
|
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
this.textConditions = text;
|
|
59
|
-
|
|
60
|
+
const own = !isEmptyPPr(tableStyle.pPr) || !isEmptyRPr(tableStyle.rPr);
|
|
61
|
+
this.formatsText = text.length > 0 || own;
|
|
62
|
+
this.styleFormatsText = own
|
|
63
|
+
|| ((_a = tableStyle.tblStylePr) !== null && _a !== void 0 ? _a : []).some((pr) => pr !== undefined && pr !== null && formatsText(pr));
|
|
60
64
|
this.key = new CellContextKey(tableStyleId, named);
|
|
61
65
|
}
|
|
62
66
|
toString() {
|
|
@@ -248,7 +252,9 @@ export class TableContextTracker {
|
|
|
248
252
|
/**
|
|
249
253
|
* Walks everything under `root` (an object, an element pair or an array of them), in document
|
|
250
254
|
* order, calling `visit` for each paragraph with its context. It descends through every
|
|
251
|
-
* member but the `PARENT` pointers, so a text box's paragraphs are reached, in no table.
|
|
255
|
+
* member but the `PARENT` pointers, so a text box's paragraphs are reached, in no table. A kept
|
|
256
|
+
* `mc:AlternateContent` is descended through its selected branch alone (`selectedBranchOf`,
|
|
257
|
+
* docx4j's `McMode.READ`), so a text box held in both branches is visited once (CR-010 phase 3).
|
|
252
258
|
*/
|
|
253
259
|
walk(root, visit) {
|
|
254
260
|
const seen = new Set();
|
|
@@ -264,6 +270,10 @@ export class TableContextTracker {
|
|
|
264
270
|
return;
|
|
265
271
|
seen.add(o);
|
|
266
272
|
const typeName = o.TYPE_NAME;
|
|
273
|
+
if (typeName === ALTERNATE_CONTENT) {
|
|
274
|
+
descend(selectedBranchOf(o), depth + 1);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
267
277
|
if (typeName === P)
|
|
268
278
|
visit(o, this.cellContext(o));
|
|
269
279
|
const leave = this.enter(o);
|
package/dist/opc/Load.d.mts
CHANGED
|
@@ -5,9 +5,16 @@ import type { OpcPackage } from '../packages/OpcPackage.mjs';
|
|
|
5
5
|
export interface LoadOptions {
|
|
6
6
|
/** Part classes by content type; defaults to docx4j's table. */
|
|
7
7
|
registry?: PartRegistry;
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Resolve `mc:AlternateContent` before unmarshalling, as Word does on open. Default false since
|
|
10
|
+
* CR-010 phase 4: every element is kept with all its branches, as docx4j keeps it since its CR-021;
|
|
11
|
+
* readers take the branch resolving would (`selectedBranchOf`), mutators every branch, and a save
|
|
12
|
+
* writes them all. Either way a part's `warnings` report where the branches' text differs. True
|
|
13
|
+
* resolves, but keeps the element under a `KEPT_HOSTS` host and one nothing can be selected from
|
|
14
|
+
* (CR-010 phase 1).
|
|
15
|
+
*/
|
|
9
16
|
mcePreprocess?: boolean;
|
|
10
|
-
/** Runs over every XML part's DOM before it is unmarshalled; set by the package from `mcePreprocess
|
|
17
|
+
/** Runs over every XML part's DOM before it is unmarshalled; set by the package from `mcePreprocess` (`mceKeep` or `mcePreprocess`). */
|
|
11
18
|
preprocessor?: DomPreprocessor;
|
|
12
19
|
}
|
|
13
20
|
/**
|
package/dist/opc/index.d.mts
CHANGED
|
@@ -6,7 +6,7 @@ export { ZipPartStore, ZipPartSink } from './ZipPartStore.mjs';
|
|
|
6
6
|
export { FlatOpcPartStore, FlatOpcPartSink, type FlatOpcSinkOptions, PKG_NS } from './FlatOpcPartStore.mjs';
|
|
7
7
|
export { loadPackage, type LoadOptions } from './Load.mjs';
|
|
8
8
|
export { savePackage } from './Save.mjs';
|
|
9
|
-
export { mcePreprocess, createMcePreprocessor, resolveAlternateContent, MCE_NS, type McePreprocessOptions } from './mce/preprocessor.mjs';
|
|
10
|
-
export { UNDERSTOOD_NAMESPACES } from './mce/understood.mjs';
|
|
9
|
+
export { mceKeep, mcePreprocess, createMcePreprocessor, resolveAlternateContent, preprocessAlternateContent, MCE_NS, KEPT_HOSTS, type McePreprocessOptions, type McBranchesDifferWarning, type PartWarning, type MceResult } from './mce/preprocessor.mjs';
|
|
10
|
+
export { UNDERSTOOD_NAMESPACES, UNDERSTOOD_PREFIXES } from './mce/understood.mjs';
|
|
11
11
|
export { Docx4JException, InvalidFormatException, PartUnrecognisedException, PartUnmarshalException, CyclicStylesException, PropertyResolverNotCreatedException } from './exceptions.mjs';
|
|
12
12
|
export { parseXml, serializeXml, serializeXmlPart, createDocument, encodeText, decodeXmlText, stripXmlDeclaration, base64Encode, base64Decode, childElements, XML_DECLARATION } from '../xml/dom.mjs';
|
package/dist/opc/index.mjs
CHANGED
|
@@ -7,7 +7,7 @@ export { ZipPartStore, ZipPartSink } from './ZipPartStore.mjs';
|
|
|
7
7
|
export { FlatOpcPartStore, FlatOpcPartSink, PKG_NS } from './FlatOpcPartStore.mjs';
|
|
8
8
|
export { loadPackage } from './Load.mjs';
|
|
9
9
|
export { savePackage } from './Save.mjs';
|
|
10
|
-
export { mcePreprocess, createMcePreprocessor, resolveAlternateContent, MCE_NS } from './mce/preprocessor.mjs';
|
|
11
|
-
export { UNDERSTOOD_NAMESPACES } from './mce/understood.mjs';
|
|
10
|
+
export { mceKeep, mcePreprocess, createMcePreprocessor, resolveAlternateContent, preprocessAlternateContent, MCE_NS, KEPT_HOSTS } from './mce/preprocessor.mjs';
|
|
11
|
+
export { UNDERSTOOD_NAMESPACES, UNDERSTOOD_PREFIXES } from './mce/understood.mjs';
|
|
12
12
|
export { Docx4JException, InvalidFormatException, PartUnrecognisedException, PartUnmarshalException, CyclicStylesException, PropertyResolverNotCreatedException } from './exceptions.mjs';
|
|
13
13
|
export { parseXml, serializeXml, serializeXmlPart, createDocument, encodeText, decodeXmlText, stripXmlDeclaration, base64Encode, base64Decode, childElements, XML_DECLARATION } from '../xml/dom.mjs';
|
|
@@ -1,15 +1,71 @@
|
|
|
1
1
|
import type { XmlPart } from '../../parts/XmlPart.mjs';
|
|
2
2
|
export declare const MCE_NS = "http://schemas.openxmlformats.org/markup-compatibility/2006";
|
|
3
|
+
/**
|
|
4
|
+
* The hosts whose mc:AlternateContent is kept whole on load, as `{namespace}localName`
|
|
5
|
+
* (CR-010 phase 1): a chart's `c:chartSpace` and SpreadsheetML's `workbook`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const KEPT_HOSTS: ReadonlySet<string>;
|
|
3
8
|
export interface McePreprocessOptions {
|
|
4
9
|
/** Namespaces counted as understood; defaults to the object model's modules. */
|
|
5
10
|
understood?: ReadonlySet<string>;
|
|
11
|
+
/** Hosts, as `{namespace}localName`, whose mc:AlternateContent is left whole; defaults to `KEPT_HOSTS`. */
|
|
12
|
+
keptHosts?: ReadonlySet<string>;
|
|
13
|
+
/**
|
|
14
|
+
* False leaves every element whole and reports what resolving would (CR-010 phase 4): the same
|
|
15
|
+
* warnings, from the same elements, and no change to the DOM. Defaults to true.
|
|
16
|
+
*/
|
|
17
|
+
resolve?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* What load reports about a part (CR-010 phase 1; the text extraction paper's warning channel).
|
|
21
|
+
* `mce.branches_differ`: an mc:AlternateContent whose branches' text differs, so a reader that
|
|
22
|
+
* takes another branch than this package's reads something else. `selected` is the branch this
|
|
23
|
+
* package took (`Choice(wps)`, `Fallback`); `branches` gives every branch's text, in document
|
|
24
|
+
* order, as the w:t, a:t and m:t text it holds, with whitespace collapsed.
|
|
25
|
+
*/
|
|
26
|
+
export interface McBranchesDifferWarning {
|
|
27
|
+
code: 'mce.branches_differ';
|
|
28
|
+
partName: string | undefined;
|
|
29
|
+
/** Where the element starts in the part, where the DOM records it (xmldom does; a browser's DOMParser does not). */
|
|
30
|
+
line?: number;
|
|
31
|
+
column?: number;
|
|
32
|
+
selected: string;
|
|
33
|
+
branches: {
|
|
34
|
+
branch: string;
|
|
35
|
+
text: string;
|
|
36
|
+
}[];
|
|
37
|
+
message: string;
|
|
38
|
+
}
|
|
39
|
+
/** A warning load records on a part. */
|
|
40
|
+
export type PartWarning = McBranchesDifferWarning;
|
|
41
|
+
/**
|
|
42
|
+
* What one pass over a DOM did. `kept` counts the elements it met and left whole; it doesn't enter a
|
|
43
|
+
* branch the rule doesn't select, so an element nested there is not counted.
|
|
44
|
+
*/
|
|
45
|
+
export interface MceResult {
|
|
46
|
+
resolved: number;
|
|
47
|
+
kept: number;
|
|
48
|
+
warnings: PartWarning[];
|
|
6
49
|
}
|
|
7
|
-
/** The default preprocessor, over the object model's namespaces. */
|
|
8
|
-
export declare function mcePreprocess(doc: Document, _part?: XmlPart<unknown>): void;
|
|
9
|
-
/** A preprocessor over a namespace set of your own. */
|
|
10
|
-
export declare function createMcePreprocessor(options: McePreprocessOptions): (doc: Document, part?: XmlPart<unknown>) => void;
|
|
11
50
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
51
|
+
* Load's preprocessor since CR-010 phase 4: every mc:AlternateContent is kept, and the part's
|
|
52
|
+
* warnings are what resolving would report. Returns them.
|
|
53
|
+
*/
|
|
54
|
+
export declare function mceKeep(doc: Document, part?: XmlPart<unknown>): PartWarning[];
|
|
55
|
+
/** The resolving preprocessor (`mcePreprocess: true`), over the object model's namespaces. Returns the part's warnings. */
|
|
56
|
+
export declare function mcePreprocess(doc: Document, part?: XmlPart<unknown>): PartWarning[];
|
|
57
|
+
/** A preprocessor over a namespace set (or kept hosts) of your own. */
|
|
58
|
+
export declare function createMcePreprocessor(options: McePreprocessOptions): (doc: Document, part?: XmlPart<unknown>) => PartWarning[];
|
|
59
|
+
/**
|
|
60
|
+
* Resolves every mc:AlternateContent under a node, outermost first, but those `KEPT_HOSTS` and the
|
|
61
|
+
* nothing-selectable rule keep (see the file comment). Returns the number resolved.
|
|
14
62
|
*/
|
|
15
63
|
export declare function resolveAlternateContent(root: Node, understood?: ReadonlySet<string>): number;
|
|
64
|
+
/**
|
|
65
|
+
* The pass itself: what it resolved, kept and reported. With `resolve: false` it visits the elements
|
|
66
|
+
* resolving would, outermost first and into the branch the rule selects, reports the same warnings,
|
|
67
|
+
* and leaves the DOM as it was.
|
|
68
|
+
*/
|
|
69
|
+
export declare function preprocessAlternateContent(root: Node, options?: McePreprocessOptions & {
|
|
70
|
+
partName?: string;
|
|
71
|
+
}): MceResult;
|