reamkit 1.15.4 → 1.17.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/dist/esm/core/bidi/segments.js +1 -2
- package/dist/esm/core/converter/facade.d.ts +5 -0
- package/dist/esm/core/converter/project.js +5 -1
- package/dist/esm/core/converter/ream.d.ts +6 -0
- package/dist/esm/core/converter/ream.js +34 -8
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +659 -7
- package/dist/esm/core/drawingml/chart-geometry.d.ts +34 -10
- package/dist/esm/core/drawingml/chart-geometry.js +424 -92
- package/dist/esm/core/drawingml/chart-parser.js +301 -11
- package/dist/esm/core/drawingml/colors.d.ts +7 -7
- package/dist/esm/core/drawingml/colors.js +84 -18
- package/dist/esm/core/drawingml/preset-geometry.js +240 -6
- package/dist/esm/core/drawingml/shape-render.d.ts +12 -0
- package/dist/esm/core/drawingml/shape-render.js +116 -3
- package/dist/esm/core/drawingml/sparkline-geometry.js +6 -4
- package/dist/esm/core/drawingml/theme-parser.d.ts +51 -0
- package/dist/esm/core/drawingml/theme-parser.js +92 -1
- package/dist/esm/core/font/measure.d.ts +5 -0
- package/dist/esm/core/font/measure.js +21 -1
- package/dist/esm/core/font/ttf-subset.js +26 -1
- package/dist/esm/core/images.d.ts +2 -2
- package/dist/esm/core/images.js +353 -43
- package/dist/esm/core/indexed-colors.d.ts +11 -0
- package/dist/esm/core/indexed-colors.js +81 -0
- package/dist/esm/core/ir/flow.d.ts +23 -1
- package/dist/esm/core/ir/sheet.d.ts +61 -0
- package/dist/esm/{excel → core}/number-format.d.ts +37 -0
- package/dist/esm/core/number-format.js +800 -0
- package/dist/esm/core/numbering/apply.d.ts +5 -3
- package/dist/esm/core/numbering/apply.js +42 -11
- package/dist/esm/core/numbering/index.js +2 -0
- package/dist/esm/core/numbering/state.d.ts +13 -1
- package/dist/esm/core/numbering/state.js +256 -15
- package/dist/esm/core/opc/alternate-content.d.ts +13 -0
- package/dist/esm/core/opc/alternate-content.js +72 -0
- package/dist/esm/core/opc/core-properties.js +1 -0
- package/dist/esm/core/opc/package.d.ts +14 -1
- package/dist/esm/core/opc/package.js +43 -4
- package/dist/esm/core/opc/relationships.js +1 -0
- package/dist/esm/core/opc/xml-entities.d.ts +12 -0
- package/dist/esm/core/opc/xml-entities.js +91 -0
- package/dist/esm/core/po-helpers.js +16 -1
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +137 -0
- package/dist/esm/core/style-cascade/resolver.js +66 -11
- package/dist/esm/core/style-cascade/table.js +35 -11
- package/dist/esm/core/style-cascade/types.d.ts +42 -1
- package/dist/esm/core/style-cascade/types.js +4 -0
- package/dist/esm/core/vector.d.ts +30 -0
- package/dist/esm/excel/activex-parser.js +1 -0
- package/dist/esm/excel/column-bands.d.ts +26 -1
- package/dist/esm/excel/column-bands.js +156 -20
- package/dist/esm/excel/comments-parser.js +5 -3
- package/dist/esm/excel/conditional-format.d.ts +25 -2
- package/dist/esm/excel/conditional-format.js +63 -19
- package/dist/esm/excel/escaped-text.d.ts +9 -0
- package/dist/esm/excel/escaped-text.js +20 -0
- package/dist/esm/excel/form-control-parser.js +1 -0
- package/dist/esm/excel/formula/dates.js +1 -1
- package/dist/esm/excel/header-footer.d.ts +9 -1
- package/dist/esm/excel/header-footer.js +162 -14
- package/dist/esm/excel/index.d.ts +1 -1
- package/dist/esm/excel/index.js +2 -2
- package/dist/esm/excel/pivot-table-parser.js +1 -0
- package/dist/esm/excel/print-model.d.ts +75 -0
- package/dist/esm/excel/print-model.js +899 -77
- package/dist/esm/excel/printer-settings.d.ts +16 -0
- package/dist/esm/excel/printer-settings.js +36 -0
- package/dist/esm/excel/rich-value.d.ts +13 -0
- package/dist/esm/excel/rich-value.js +120 -0
- package/dist/esm/excel/shared-strings-parser.js +11 -7
- package/dist/esm/excel/sheet-drawing.d.ts +11 -0
- package/dist/esm/excel/sheet-drawing.js +26 -3
- package/dist/esm/excel/sheet-shape-parser.d.ts +53 -1
- package/dist/esm/excel/sheet-shape-parser.js +461 -32
- package/dist/esm/excel/sheet-to-flow.d.ts +23 -2
- package/dist/esm/excel/sheet-to-flow.js +553 -36
- package/dist/esm/excel/slicer-parser.js +1 -0
- package/dist/esm/excel/styles-parser.d.ts +30 -2
- package/dist/esm/excel/styles-parser.js +177 -40
- package/dist/esm/excel/table-parser.js +1 -0
- package/dist/esm/excel/vml-drawing.d.ts +68 -0
- package/dist/esm/excel/vml-drawing.js +158 -0
- package/dist/esm/excel/workbook-parser.d.ts +5 -0
- package/dist/esm/excel/workbook-parser.js +5 -1
- package/dist/esm/excel/worksheet-parser.d.ts +2 -1
- package/dist/esm/excel/worksheet-parser.js +150 -61
- package/dist/esm/excel/xlsx-reader.js +242 -11
- package/dist/esm/excel/xlsx-to-pdf.js +12 -1
- package/dist/esm/html/html-writer.js +29 -6
- package/dist/esm/layout/page-doc.d.ts +87 -1
- package/dist/esm/layout/styled-layout.d.ts +46 -2
- package/dist/esm/layout/styled-layout.js +2345 -274
- package/dist/esm/pdf/cid-font.js +20 -9
- package/dist/esm/pdf/objects.d.ts +13 -0
- package/dist/esm/pdf/objects.js +18 -1
- package/dist/esm/pdf/shading.js +76 -6
- package/dist/esm/pdf/styled-page-emitter.js +355 -49
- package/dist/esm/pdf/text-page-renderer.js +1 -0
- package/dist/esm/pdf/vector-graphics.d.ts +31 -2
- package/dist/esm/pdf/vector-graphics.js +103 -3
- package/dist/esm/pptx/pptx-reader.js +2 -0
- package/dist/esm/pptx/slide-parser.d.ts +21 -4
- package/dist/esm/pptx/slide-parser.js +98 -24
- package/dist/esm/svg/svg-writer.js +23 -4
- package/dist/esm/word/document-parser.d.ts +96 -7
- package/dist/esm/word/document-parser.js +538 -116
- package/dist/esm/word/docx-reader.js +135 -43
- package/dist/esm/word/docx-writer.js +6 -1
- package/dist/esm/word/drawing-parser.d.ts +86 -5
- package/dist/esm/word/drawing-parser.js +1590 -54
- package/dist/esm/word/index.d.ts +4 -4
- package/dist/esm/word/numbering-parser.d.ts +2 -1
- package/dist/esm/word/numbering-parser.js +108 -21
- package/dist/esm/word/paragraph-properties.d.ts +11 -1
- package/dist/esm/word/paragraph-properties.js +219 -10
- package/dist/esm/word/run-properties.js +45 -1
- package/dist/esm/word/settings-parser.d.ts +24 -0
- package/dist/esm/word/settings-parser.js +32 -5
- package/dist/esm/word/shading.d.ts +11 -0
- package/dist/esm/word/shading.js +64 -0
- package/dist/esm/word/styles-parser.js +9 -2
- package/dist/esm/word/table-parser.d.ts +9 -1
- package/dist/esm/word/table-parser.js +182 -30
- package/package.json +7 -3
- package/dist/esm/excel/number-format.js +0 -476
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BodyElement, Numbering } from '../document-model/index.js';
|
|
1
|
+
import { BodyElement, Numbering, StyleSheet } from '../document-model/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Apply list numbering to a body as a FlowDoc transform (§17.9): walk the
|
|
4
4
|
* elements (recursing into table cells), advancing list counters and prepending
|
|
@@ -8,9 +8,11 @@ import { BodyElement, Numbering } from '../document-model/index.js';
|
|
|
8
8
|
*
|
|
9
9
|
* @param body The body elements to transform.
|
|
10
10
|
* @param numbering The parsed numbering definitions, or `undefined`.
|
|
11
|
+
* @param styles The style sheet, for a paragraph whose numbering comes from
|
|
12
|
+
* its STYLE (§17.9.24) rather than its own `w:numPr`.
|
|
11
13
|
* @returns A new body with markers materialized.
|
|
12
14
|
*/
|
|
13
|
-
export declare function applyNumbering(body: ReadonlyArray<BodyElement>, numbering: Numbering | undefined): Array<BodyElement>;
|
|
15
|
+
export declare function applyNumbering(body: ReadonlyArray<BodyElement>, numbering: Numbering | undefined, styles?: StyleSheet): Array<BodyElement>;
|
|
14
16
|
/**
|
|
15
17
|
* Apply {@link applyNumbering} to each header/footer band independently (each
|
|
16
18
|
* gets its own counter state), keyed as in the input map.
|
|
@@ -19,4 +21,4 @@ export declare function applyNumbering(body: ReadonlyArray<BodyElement>, numberi
|
|
|
19
21
|
* @param numbering The parsed numbering definitions, or `undefined`.
|
|
20
22
|
* @returns A new map with numbering applied (empty when `hf` is empty/absent).
|
|
21
23
|
*/
|
|
22
|
-
export declare function applyNumberingToHeadersFooters(hf: ReadonlyMap<string, ReadonlyArray<BodyElement>> | undefined, numbering: Numbering | undefined): ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|
|
24
|
+
export declare function applyNumberingToHeadersFooters(hf: ReadonlyMap<string, ReadonlyArray<BodyElement>> | undefined, numbering: Numbering | undefined, styles?: StyleSheet): ReadonlyMap<string, ReadonlyArray<BodyElement>>;
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { pt } from "../ir/units.js";
|
|
2
|
+
import { NumberingState, effectiveAbstract } from "./state.js";
|
|
3
|
+
import { resolveParagraphProperties } from "../style-cascade/resolver.js";
|
|
4
|
+
import "../style-cascade/index.js";
|
|
2
5
|
//#region src/core/numbering/apply.ts
|
|
6
|
+
/** The height a picture bullet takes when neither the level nor the paragraph
|
|
7
|
+
* mark states a font size — Word's own default text size. */
|
|
8
|
+
var DEFAULT_BULLET_HEIGHT_PT = 11;
|
|
9
|
+
/** SOFT HYPHEN, which a `w:lvlText` may spell a dash bullet with (§17.9.11). */
|
|
10
|
+
var SOFT_HYPHEN = "";
|
|
3
11
|
/**
|
|
4
12
|
* Apply list numbering to a body as a FlowDoc transform (§17.9): walk the
|
|
5
13
|
* elements (recursing into table cells), advancing list counters and prepending
|
|
@@ -9,28 +17,51 @@ import { NumberingState } from "./state.js";
|
|
|
9
17
|
*
|
|
10
18
|
* @param body The body elements to transform.
|
|
11
19
|
* @param numbering The parsed numbering definitions, or `undefined`.
|
|
20
|
+
* @param styles The style sheet, for a paragraph whose numbering comes from
|
|
21
|
+
* its STYLE (§17.9.24) rather than its own `w:numPr`.
|
|
12
22
|
* @returns A new body with markers materialized.
|
|
13
23
|
*/
|
|
14
|
-
function applyNumbering(body, numbering) {
|
|
24
|
+
function applyNumbering(body, numbering, styles) {
|
|
15
25
|
if (!numbering || numbering.abstractNums.size === 0) return body.map((b) => b);
|
|
16
26
|
const state = new NumberingState();
|
|
27
|
+
const numberingOf = (p) => p.properties.numbering ?? (styles ? resolveParagraphProperties(p.properties, styles).numbering : void 0);
|
|
17
28
|
const transformParagraph = (p) => {
|
|
18
|
-
|
|
29
|
+
if (p.properties.sectionBreak === true && p.runs.every((r) => r.text === "")) return p;
|
|
30
|
+
const ref = numberingOf(p);
|
|
19
31
|
if (!ref) return p;
|
|
20
32
|
const marker = state.resolveMarker(numbering, ref);
|
|
21
33
|
if (marker === null) return p;
|
|
22
34
|
const instance = numbering.numInstances.get(ref.numId);
|
|
23
|
-
const level = (instance ? numbering
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
const level = (instance ? effectiveAbstract(numbering, instance) : void 0)?.levels.get(ref.ilvl);
|
|
36
|
+
const pic = level?.picBullet;
|
|
37
|
+
const bulletCapPt = level?.runProperties.fontSizePt ?? p.properties.runProperties?.fontSizePt ?? DEFAULT_BULLET_HEIGHT_PT;
|
|
38
|
+
const bulletScale = pic ? Math.min(1, bulletCapPt / Math.max(1, pic.heightPt)) : 1;
|
|
39
|
+
const markerRuns = pic ? [{
|
|
40
|
+
text: "",
|
|
41
|
+
properties: {},
|
|
42
|
+
listMarker: true,
|
|
43
|
+
inlineImage: {
|
|
44
|
+
resource: pic.resource,
|
|
45
|
+
width: pt(pic.widthPt * bulletScale),
|
|
46
|
+
height: pt(pic.heightPt * bulletScale)
|
|
47
|
+
}
|
|
48
|
+
}, {
|
|
49
|
+
text: " ",
|
|
50
|
+
properties: {},
|
|
27
51
|
listMarker: true
|
|
28
|
-
}
|
|
52
|
+
}] : [{
|
|
53
|
+
text: `${marker.replaceAll(SOFT_HYPHEN, "-")}\t`,
|
|
54
|
+
properties: {
|
|
55
|
+
...p.properties.runProperties,
|
|
56
|
+
...level?.runProperties
|
|
57
|
+
},
|
|
58
|
+
listMarker: true
|
|
59
|
+
}];
|
|
29
60
|
const newProps = mergeIndentFromLevel(p.properties, level?.paragraphProperties);
|
|
30
61
|
return {
|
|
31
62
|
...p,
|
|
32
63
|
properties: newProps,
|
|
33
|
-
runs: [
|
|
64
|
+
runs: [...markerRuns, ...p.runs]
|
|
34
65
|
};
|
|
35
66
|
};
|
|
36
67
|
const visit = (el) => {
|
|
@@ -73,11 +104,11 @@ function mergeIndentFromLevel(paragraphProps, levelProps) {
|
|
|
73
104
|
* @param numbering The parsed numbering definitions, or `undefined`.
|
|
74
105
|
* @returns A new map with numbering applied (empty when `hf` is empty/absent).
|
|
75
106
|
*/
|
|
76
|
-
function applyNumberingToHeadersFooters(hf, numbering) {
|
|
107
|
+
function applyNumberingToHeadersFooters(hf, numbering, styles) {
|
|
77
108
|
if (!hf || hf.size === 0) return /* @__PURE__ */ new Map();
|
|
78
109
|
if (!numbering || numbering.abstractNums.size === 0) return hf;
|
|
79
110
|
const out = /* @__PURE__ */ new Map();
|
|
80
|
-
for (const [key, value] of hf) out.set(key, applyNumbering(value, numbering));
|
|
111
|
+
for (const [key, value] of hf) out.set(key, applyNumbering(value, numbering, styles));
|
|
81
112
|
return out;
|
|
82
113
|
}
|
|
83
114
|
//#endregion
|
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
import { AbstractNumbering, Numbering, NumberingLevel, NumberingReference } from '../document-model/index.js';
|
|
1
|
+
import { AbstractNumbering, Numbering, NumberingInstance, NumberingLevel, NumberingReference } from '../document-model/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* §17.9.27 — the levels an INSTANCE actually numbers by: its abstract
|
|
4
|
+
* definition's, with any `w:lvlOverride/w:lvl` shadowing them. Cached per
|
|
5
|
+
* instance, since every list paragraph asks for it.
|
|
6
|
+
*
|
|
7
|
+
* @param numbering The parsed numbering definitions.
|
|
8
|
+
* @param instance The `w:num` the paragraph refers to.
|
|
9
|
+
* @returns The effective abstract numbering, or `undefined` when the instance
|
|
10
|
+
* names one that is not there.
|
|
11
|
+
*/
|
|
12
|
+
export declare function effectiveAbstract(numbering: Numbering, instance: NumberingInstance): AbstractNumbering | undefined;
|
|
2
13
|
/**
|
|
3
14
|
* Mutable list-counter state for one numbering scope (e.g. a body or a single
|
|
4
15
|
* header/footer band). Tracks a counter per list per level and advances them as
|
|
@@ -6,6 +17,7 @@ import { AbstractNumbering, Numbering, NumberingLevel, NumberingReference } from
|
|
|
6
17
|
*/
|
|
7
18
|
export declare class NumberingState {
|
|
8
19
|
private readonly counters;
|
|
20
|
+
private readonly restarted;
|
|
9
21
|
/**
|
|
10
22
|
* Advance the counter for `ref` (resetting deeper levels) and format its
|
|
11
23
|
* marker text. The first hit at a level seeds it from the level's `start`;
|
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
//#region src/core/numbering/state.ts
|
|
2
2
|
/**
|
|
3
|
+
* §17.9.27 — the levels an INSTANCE actually numbers by: its abstract
|
|
4
|
+
* definition's, with any `w:lvlOverride/w:lvl` shadowing them. Cached per
|
|
5
|
+
* instance, since every list paragraph asks for it.
|
|
6
|
+
*
|
|
7
|
+
* @param numbering The parsed numbering definitions.
|
|
8
|
+
* @param instance The `w:num` the paragraph refers to.
|
|
9
|
+
* @returns The effective abstract numbering, or `undefined` when the instance
|
|
10
|
+
* names one that is not there.
|
|
11
|
+
*/
|
|
12
|
+
function effectiveAbstract(numbering, instance) {
|
|
13
|
+
const base = numbering.abstractNums.get(instance.abstractNumId);
|
|
14
|
+
if (!base || !instance.levelOverrides || instance.levelOverrides.size === 0) return base;
|
|
15
|
+
const hit = effectiveCache.get(instance);
|
|
16
|
+
if (hit) return hit;
|
|
17
|
+
const levels = new Map(base.levels);
|
|
18
|
+
for (const [ilvl, lvl] of instance.levelOverrides) levels.set(ilvl, lvl);
|
|
19
|
+
const merged = {
|
|
20
|
+
id: base.id,
|
|
21
|
+
levels
|
|
22
|
+
};
|
|
23
|
+
effectiveCache.set(instance, merged);
|
|
24
|
+
return merged;
|
|
25
|
+
}
|
|
26
|
+
var effectiveCache = /* @__PURE__ */ new WeakMap();
|
|
27
|
+
/**
|
|
3
28
|
* Mutable list-counter state for one numbering scope (e.g. a body or a single
|
|
4
29
|
* header/footer band). Tracks a counter per list per level and advances them as
|
|
5
30
|
* paragraphs are visited in order.
|
|
6
31
|
*/
|
|
7
32
|
var NumberingState = class {
|
|
8
33
|
counters = /* @__PURE__ */ new Map();
|
|
34
|
+
restarted = /* @__PURE__ */ new Set();
|
|
9
35
|
/**
|
|
10
36
|
* Advance the counter for `ref` (resetting deeper levels) and format its
|
|
11
37
|
* marker text. The first hit at a level seeds it from the level's `start`;
|
|
@@ -19,19 +45,27 @@ var NumberingState = class {
|
|
|
19
45
|
resolveMarker(numbering, ref) {
|
|
20
46
|
const instance = numbering.numInstances.get(ref.numId);
|
|
21
47
|
if (!instance) return null;
|
|
22
|
-
const abstractNum = numbering
|
|
48
|
+
const abstractNum = effectiveAbstract(numbering, instance);
|
|
23
49
|
if (!abstractNum) return null;
|
|
24
50
|
const level = abstractNum.levels.get(ref.ilvl);
|
|
25
51
|
if (!level) return null;
|
|
26
|
-
let arr = this.counters.get(
|
|
52
|
+
let arr = this.counters.get(instance.abstractNumId);
|
|
27
53
|
if (!arr) {
|
|
28
|
-
arr = new Array(9).fill(0);
|
|
29
|
-
this.counters.set(
|
|
54
|
+
arr = new Array(9).fill(void 0);
|
|
55
|
+
this.counters.set(instance.abstractNumId, arr);
|
|
56
|
+
}
|
|
57
|
+
for (let k = ref.ilvl + 1; k < arr.length; k++) arr[k] = void 0;
|
|
58
|
+
const startAt = (i, fallback) => instance.startOverrides?.get(i) ?? fallback ?? 0;
|
|
59
|
+
const restartKey = `${ref.numId}:${String(ref.ilvl)}`;
|
|
60
|
+
const override = instance.startOverrides?.get(ref.ilvl);
|
|
61
|
+
if (override !== void 0 && !this.restarted.has(restartKey)) {
|
|
62
|
+
this.restarted.add(restartKey);
|
|
63
|
+
arr[ref.ilvl] = override;
|
|
64
|
+
} else {
|
|
65
|
+
const current = arr[ref.ilvl];
|
|
66
|
+
arr[ref.ilvl] = current === void 0 ? startAt(ref.ilvl, level.start) : current + 1;
|
|
30
67
|
}
|
|
31
|
-
|
|
32
|
-
if (arr[ref.ilvl] === 0) arr[ref.ilvl] = level.start;
|
|
33
|
-
else arr[ref.ilvl] += 1;
|
|
34
|
-
return formatLevelMarker(abstractNum, level, arr);
|
|
68
|
+
return formatLevelMarker(abstractNum, level, arr.map((n, i) => n ?? startAt(i, abstractNum.levels.get(i)?.start)));
|
|
35
69
|
}
|
|
36
70
|
};
|
|
37
71
|
function formatLevelMarker(abstractNum, currentLevel, counters) {
|
|
@@ -40,20 +74,227 @@ function formatLevelMarker(abstractNum, currentLevel, counters) {
|
|
|
40
74
|
const lvlIdx = Number(n) - 1;
|
|
41
75
|
const level = abstractNum.levels.get(lvlIdx);
|
|
42
76
|
const counter = counters[lvlIdx] ?? 0;
|
|
43
|
-
return formatCounter(level?.format ?? "decimal", counter);
|
|
77
|
+
return formatCounter(currentLevel.isLegal === true && lvlIdx !== currentLevel.ilvl ? "decimal" : level?.format ?? "decimal", counter);
|
|
44
78
|
});
|
|
45
79
|
}
|
|
46
80
|
function formatCounter(format, n) {
|
|
47
|
-
if (n
|
|
81
|
+
if (n < 0) return "";
|
|
48
82
|
switch (format) {
|
|
49
83
|
case "decimal": return String(n);
|
|
50
|
-
case "
|
|
51
|
-
case "
|
|
52
|
-
case "
|
|
53
|
-
case "
|
|
84
|
+
case "decimalZero": return n < 10 ? `0${String(n)}` : String(n);
|
|
85
|
+
case "decimalFullWidth": return digitByDigit(n, FULL_WIDTH_DIGITS);
|
|
86
|
+
case "ordinal": return `${String(n)}${ordinalSuffix(n)}`;
|
|
87
|
+
case "lowerLetter": return n === 0 ? "" : toLetters(n).toLowerCase();
|
|
88
|
+
case "upperLetter": return n === 0 ? "" : toLetters(n);
|
|
89
|
+
case "lowerRoman": return n === 0 ? "" : toRoman(n).toLowerCase();
|
|
90
|
+
case "upperRoman": return n === 0 ? "" : toRoman(n);
|
|
91
|
+
case "ideographTraditional": return n === 0 ? "" : n <= HEAVENLY_STEMS.length ? HEAVENLY_STEMS[n - 1] : String(n);
|
|
92
|
+
case "ideographZodiac": return n === 0 ? "" : n <= EARTHLY_BRANCHES.length ? EARTHLY_BRANCHES[n - 1] : String(n);
|
|
93
|
+
case "ideographDigital":
|
|
94
|
+
case "koreanDigital2": return digitByDigit(n, IDEOGRAPH_DIGITS);
|
|
95
|
+
case "ideographLegalTraditional": return counting(n, LEGAL_DIGITS, LEGAL_UNITS, false);
|
|
96
|
+
case "chineseCounting":
|
|
97
|
+
case "chineseCountingThousand":
|
|
98
|
+
case "japaneseCounting":
|
|
99
|
+
case "koreanCounting":
|
|
100
|
+
case "taiwaneseCounting":
|
|
101
|
+
case "taiwaneseCountingThousand": return counting(n, IDEOGRAPH_DIGITS, COUNTING_UNITS, true);
|
|
102
|
+
case "hebrew1": return n === 0 ? "" : toHebrewNumeral(n);
|
|
103
|
+
case "hebrew2": return n === 0 ? "" : HEBREW_ALPHABET[(n - 1) % HEBREW_ALPHABET.length];
|
|
104
|
+
case "decimalEnclosedCircle": return n >= 1 && n <= 20 ? String.fromCodePoint(9311 + n) : String(n);
|
|
105
|
+
case "chicago":
|
|
106
|
+
if (n < 1) return "";
|
|
107
|
+
return CHICAGO_MARKS[(n - 1) % CHICAGO_MARKS.length].repeat(Math.floor((n - 1) / CHICAGO_MARKS.length) + 1);
|
|
54
108
|
default: return "";
|
|
55
109
|
}
|
|
56
110
|
}
|
|
111
|
+
var HEBREW_ONES = [
|
|
112
|
+
"",
|
|
113
|
+
"א",
|
|
114
|
+
"ב",
|
|
115
|
+
"ג",
|
|
116
|
+
"ד",
|
|
117
|
+
"ה",
|
|
118
|
+
"ו",
|
|
119
|
+
"ז",
|
|
120
|
+
"ח",
|
|
121
|
+
"ט"
|
|
122
|
+
];
|
|
123
|
+
var HEBREW_TENS = [
|
|
124
|
+
"",
|
|
125
|
+
"י",
|
|
126
|
+
"כ",
|
|
127
|
+
"ל",
|
|
128
|
+
"מ",
|
|
129
|
+
"נ",
|
|
130
|
+
"ס",
|
|
131
|
+
"ע",
|
|
132
|
+
"פ",
|
|
133
|
+
"צ"
|
|
134
|
+
];
|
|
135
|
+
var HEBREW_HUNDREDS = [
|
|
136
|
+
"",
|
|
137
|
+
"ק",
|
|
138
|
+
"ר",
|
|
139
|
+
"ש",
|
|
140
|
+
"ת"
|
|
141
|
+
];
|
|
142
|
+
var HEBREW_ALPHABET = [
|
|
143
|
+
"א",
|
|
144
|
+
"ב",
|
|
145
|
+
"ג",
|
|
146
|
+
"ד",
|
|
147
|
+
"ה",
|
|
148
|
+
"ו",
|
|
149
|
+
"ז",
|
|
150
|
+
"ח",
|
|
151
|
+
"ט",
|
|
152
|
+
"י",
|
|
153
|
+
"כ",
|
|
154
|
+
"ל",
|
|
155
|
+
"מ",
|
|
156
|
+
"נ",
|
|
157
|
+
"ס",
|
|
158
|
+
"ע",
|
|
159
|
+
"פ",
|
|
160
|
+
"צ",
|
|
161
|
+
"ק",
|
|
162
|
+
"ר",
|
|
163
|
+
"ש",
|
|
164
|
+
"ת"
|
|
165
|
+
];
|
|
166
|
+
function toHebrewNumeral(n) {
|
|
167
|
+
let rest = n;
|
|
168
|
+
let out = "";
|
|
169
|
+
while (rest >= 400) {
|
|
170
|
+
out += "ת";
|
|
171
|
+
rest -= 400;
|
|
172
|
+
}
|
|
173
|
+
out += HEBREW_HUNDREDS[Math.floor(rest / 100)] ?? "";
|
|
174
|
+
rest %= 100;
|
|
175
|
+
if (rest === 15 || rest === 16) out += rest === 15 ? "טו" : "טז";
|
|
176
|
+
else {
|
|
177
|
+
out += HEBREW_TENS[Math.floor(rest / 10)] ?? "";
|
|
178
|
+
out += HEBREW_ONES[rest % 10] ?? "";
|
|
179
|
+
}
|
|
180
|
+
return out;
|
|
181
|
+
}
|
|
182
|
+
var CHICAGO_MARKS = [
|
|
183
|
+
"*",
|
|
184
|
+
"†",
|
|
185
|
+
"‡",
|
|
186
|
+
"§"
|
|
187
|
+
];
|
|
188
|
+
var IDEOGRAPH_DIGITS = [
|
|
189
|
+
"零",
|
|
190
|
+
"一",
|
|
191
|
+
"二",
|
|
192
|
+
"三",
|
|
193
|
+
"四",
|
|
194
|
+
"五",
|
|
195
|
+
"六",
|
|
196
|
+
"七",
|
|
197
|
+
"八",
|
|
198
|
+
"九"
|
|
199
|
+
];
|
|
200
|
+
var COUNTING_UNITS = [
|
|
201
|
+
"",
|
|
202
|
+
"十",
|
|
203
|
+
"百",
|
|
204
|
+
"千"
|
|
205
|
+
];
|
|
206
|
+
var LEGAL_DIGITS = [
|
|
207
|
+
"零",
|
|
208
|
+
"壹",
|
|
209
|
+
"貳",
|
|
210
|
+
"參",
|
|
211
|
+
"肆",
|
|
212
|
+
"伍",
|
|
213
|
+
"陸",
|
|
214
|
+
"柒",
|
|
215
|
+
"捌",
|
|
216
|
+
"玖"
|
|
217
|
+
];
|
|
218
|
+
var LEGAL_UNITS = [
|
|
219
|
+
"",
|
|
220
|
+
"拾",
|
|
221
|
+
"佰",
|
|
222
|
+
"仟"
|
|
223
|
+
];
|
|
224
|
+
var HEAVENLY_STEMS = [
|
|
225
|
+
"甲",
|
|
226
|
+
"乙",
|
|
227
|
+
"丙",
|
|
228
|
+
"丁",
|
|
229
|
+
"戊",
|
|
230
|
+
"己",
|
|
231
|
+
"庚",
|
|
232
|
+
"辛",
|
|
233
|
+
"壬",
|
|
234
|
+
"癸"
|
|
235
|
+
];
|
|
236
|
+
var EARTHLY_BRANCHES = [
|
|
237
|
+
"子",
|
|
238
|
+
"丑",
|
|
239
|
+
"寅",
|
|
240
|
+
"卯",
|
|
241
|
+
"辰",
|
|
242
|
+
"巳",
|
|
243
|
+
"午",
|
|
244
|
+
"未",
|
|
245
|
+
"申",
|
|
246
|
+
"酉",
|
|
247
|
+
"戌",
|
|
248
|
+
"亥"
|
|
249
|
+
];
|
|
250
|
+
var FULL_WIDTH_DIGITS = [
|
|
251
|
+
"0",
|
|
252
|
+
"1",
|
|
253
|
+
"2",
|
|
254
|
+
"3",
|
|
255
|
+
"4",
|
|
256
|
+
"5",
|
|
257
|
+
"6",
|
|
258
|
+
"7",
|
|
259
|
+
"8",
|
|
260
|
+
"9"
|
|
261
|
+
];
|
|
262
|
+
/** Each decimal digit as its own glyph — no units, no elision (10 → 一零). */
|
|
263
|
+
function digitByDigit(n, digits) {
|
|
264
|
+
return String(n).split("").map((d) => digits[Number(d)] ?? d).join("");
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* The counting systems: digits carrying place units (十百千). `elideTen` drops
|
|
268
|
+
* the leading 一 of the tens place, which is what the plain counting systems do
|
|
269
|
+
* (11 → 十一) and the formal one does not (11 → 壹拾壹). Above the units the
|
|
270
|
+
* table covers, plain digits — a list marker never gets that far.
|
|
271
|
+
*/
|
|
272
|
+
function counting(n, digits, units, elideTen) {
|
|
273
|
+
if (n >= 10 ** units.length) return String(n);
|
|
274
|
+
const ds = String(n).split("").map(Number);
|
|
275
|
+
let out = "";
|
|
276
|
+
ds.forEach((d, i) => {
|
|
277
|
+
const place = ds.length - 1 - i;
|
|
278
|
+
if (d === 0) {
|
|
279
|
+
if (out.length > 0 && !out.endsWith(digits[0]) && ds.slice(i).some((x) => x > 0)) out += digits[0];
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const head = elideTen && d === 1 && place === 1 && i === 0 ? "" : digits[d];
|
|
283
|
+
out += head + units[place];
|
|
284
|
+
});
|
|
285
|
+
return out;
|
|
286
|
+
}
|
|
287
|
+
/** `1st`, `2nd`, `3rd`, `4th` — the English ordinal suffixes (§17.18.59). */
|
|
288
|
+
function ordinalSuffix(n) {
|
|
289
|
+
const two = n % 100;
|
|
290
|
+
if (two >= 11 && two <= 13) return "th";
|
|
291
|
+
switch (n % 10) {
|
|
292
|
+
case 1: return "st";
|
|
293
|
+
case 2: return "nd";
|
|
294
|
+
case 3: return "rd";
|
|
295
|
+
default: return "th";
|
|
296
|
+
}
|
|
297
|
+
}
|
|
57
298
|
function toLetters(n) {
|
|
58
299
|
let s = "";
|
|
59
300
|
let v = n;
|
|
@@ -95,4 +336,4 @@ function normalizeBullet(lvlText) {
|
|
|
95
336
|
return lvlText;
|
|
96
337
|
}
|
|
97
338
|
//#endregion
|
|
98
|
-
export { NumberingState };
|
|
339
|
+
export { NumberingState, effectiveAbstract, formatLevelMarker };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replace every `<mc:AlternateContent>` with the markup its `<mc:Fallback>`
|
|
3
|
+
* holds, so the part reaching the XML parser is plain OOXML in its original
|
|
4
|
+
* element order. A block with no Fallback resolves to nothing — which is what
|
|
5
|
+
* §10.2 of the Markup Compatibility spec asks of a reader that understands
|
|
6
|
+
* none of the offered choices.
|
|
7
|
+
*
|
|
8
|
+
* Text with no AlternateContent is returned unchanged, which is most files.
|
|
9
|
+
*
|
|
10
|
+
* @param xml The decoded part, as read from the package.
|
|
11
|
+
* @returns The part with its compatibility blocks resolved.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveAlternateContent(xml: string): string;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
//#region src/core/opc/alternate-content.ts
|
|
2
|
+
/** Deepest nesting of one AlternateContent inside another that we will unwrap. */
|
|
3
|
+
var MAX_DEPTH = 8;
|
|
4
|
+
var OPEN = /<(?:[A-Za-z_][\w.-]*:)?AlternateContent(?=[\s/>])/g;
|
|
5
|
+
/**
|
|
6
|
+
* Find the element starting at `from` (the index of its `<`) and return the
|
|
7
|
+
* span of its content plus the index just past its closing tag. Returns
|
|
8
|
+
* undefined for an unclosed element — malformed markup we leave alone.
|
|
9
|
+
*/
|
|
10
|
+
function elementAt(xml, from, local) {
|
|
11
|
+
const nameEnd = xml.indexOf(">", from);
|
|
12
|
+
if (nameEnd < 0) return void 0;
|
|
13
|
+
if (xml[nameEnd - 1] === "/") return {
|
|
14
|
+
inner: "",
|
|
15
|
+
end: nameEnd + 1
|
|
16
|
+
};
|
|
17
|
+
const open = new RegExp(`<(?:[A-Za-z_][\\w.-]*:)?${local}(?=[\\s/>])`, "g");
|
|
18
|
+
const close = new RegExp(`</(?:[A-Za-z_][\\w.-]*:)?${local}\\s*>`, "g");
|
|
19
|
+
let depth = 1;
|
|
20
|
+
let cursor = nameEnd + 1;
|
|
21
|
+
while (depth > 0) {
|
|
22
|
+
close.lastIndex = cursor;
|
|
23
|
+
const shut = close.exec(xml);
|
|
24
|
+
if (!shut) return void 0;
|
|
25
|
+
open.lastIndex = cursor;
|
|
26
|
+
for (let o = open.exec(xml); o && o.index < shut.index; o = open.exec(xml)) if (xml[xml.indexOf(">", o.index) - 1] !== "/") depth++;
|
|
27
|
+
depth--;
|
|
28
|
+
cursor = shut.index + shut[0].length;
|
|
29
|
+
if (depth === 0) return {
|
|
30
|
+
inner: xml.slice(nameEnd + 1, shut.index),
|
|
31
|
+
end: cursor
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** The content of the first `<mc:Fallback>` directly inside `inner`, or ''. */
|
|
36
|
+
function fallbackOf(inner) {
|
|
37
|
+
const at = (/* @__PURE__ */ new RegExp("<(?:[A-Za-z_][\\w.-]*:)?Fallback(?=[\\s/>])")).exec(inner);
|
|
38
|
+
if (!at) return "";
|
|
39
|
+
return elementAt(inner, at.index, "Fallback")?.inner ?? "";
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Replace every `<mc:AlternateContent>` with the markup its `<mc:Fallback>`
|
|
43
|
+
* holds, so the part reaching the XML parser is plain OOXML in its original
|
|
44
|
+
* element order. A block with no Fallback resolves to nothing — which is what
|
|
45
|
+
* §10.2 of the Markup Compatibility spec asks of a reader that understands
|
|
46
|
+
* none of the offered choices.
|
|
47
|
+
*
|
|
48
|
+
* Text with no AlternateContent is returned unchanged, which is most files.
|
|
49
|
+
*
|
|
50
|
+
* @param xml The decoded part, as read from the package.
|
|
51
|
+
* @returns The part with its compatibility blocks resolved.
|
|
52
|
+
*/
|
|
53
|
+
function resolveAlternateContent(xml) {
|
|
54
|
+
let out = xml;
|
|
55
|
+
for (let pass = 0; pass < MAX_DEPTH; pass++) {
|
|
56
|
+
if (!out.includes("AlternateContent")) return out;
|
|
57
|
+
let cursor = 0;
|
|
58
|
+
let next = "";
|
|
59
|
+
OPEN.lastIndex = 0;
|
|
60
|
+
for (let found = OPEN.exec(out); found; found = OPEN.exec(out)) {
|
|
61
|
+
const el = elementAt(out, found.index, "AlternateContent");
|
|
62
|
+
if (!el) break;
|
|
63
|
+
next += out.slice(cursor, found.index) + fallbackOf(el.inner);
|
|
64
|
+
cursor = el.end;
|
|
65
|
+
OPEN.lastIndex = cursor;
|
|
66
|
+
}
|
|
67
|
+
out = next + out.slice(cursor);
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { resolveAlternateContent };
|
|
@@ -28,6 +28,7 @@ export declare class OpcPackage {
|
|
|
28
28
|
*/
|
|
29
29
|
private constructor();
|
|
30
30
|
private readonly relsCache;
|
|
31
|
+
private folded;
|
|
31
32
|
/**
|
|
32
33
|
* Unzip and validate a package's bytes. Rejects an OLE compound file (an
|
|
33
34
|
* encrypted OOXML container or a legacy binary `.doc`/`.xls`) and enforces the
|
|
@@ -37,8 +38,20 @@ export declare class OpcPackage {
|
|
|
37
38
|
* `_rels/.rels` is missing.
|
|
38
39
|
*/
|
|
39
40
|
static open(buffer: Uint8Array, options?: OpcOpenOptions): OpcPackage;
|
|
40
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* The bytes of the part at `path`, or undefined when absent.
|
|
43
|
+
*
|
|
44
|
+
* OPC part names are compared case-insensitively (ISO/IEC 29500-2 §9.1.1.1),
|
|
45
|
+
* so two names differing only in case are the SAME part. Producers do mix
|
|
46
|
+
* them: 123233_charts.xlsx writes `xl/worksheets/Sheet1.xml` and its
|
|
47
|
+
* relationships as `_rels/sheet1.xml.rels`, and an exact lookup found no
|
|
48
|
+
* relationships for that sheet at all — which cost it four charts, silently,
|
|
49
|
+
* because a sheet with no drawing rel is indistinguishable from one with no
|
|
50
|
+
* drawing. An exact hit still wins; the fold is only a fallback.
|
|
51
|
+
*/
|
|
41
52
|
getPart(path: string): Uint8Array | undefined;
|
|
53
|
+
/** Parts keyed by their case-folded path, built once on first miss. */
|
|
54
|
+
private foldedParts;
|
|
42
55
|
/**
|
|
43
56
|
* The bytes of the part at `path`.
|
|
44
57
|
*
|
|
@@ -19,6 +19,7 @@ var OpcPackage = class OpcPackage {
|
|
|
19
19
|
this.rootRelationships = rootRelationships;
|
|
20
20
|
}
|
|
21
21
|
relsCache = /* @__PURE__ */ new Map();
|
|
22
|
+
folded;
|
|
22
23
|
/**
|
|
23
24
|
* Unzip and validate a package's bytes. Rejects an OLE compound file (an
|
|
24
25
|
* encrypted OOXML container or a legacy binary `.doc`/`.xls`) and enforces the
|
|
@@ -37,7 +38,7 @@ var OpcPackage = class OpcPackage {
|
|
|
37
38
|
let total = 0;
|
|
38
39
|
let count = 0;
|
|
39
40
|
let violation;
|
|
40
|
-
const entries =
|
|
41
|
+
const entries = unzipGuarded(buffer, { filter: (info) => {
|
|
41
42
|
if (++count > maxEntries) {
|
|
42
43
|
violation ??= `more than ${maxEntries} entries`;
|
|
43
44
|
return false;
|
|
@@ -61,9 +62,32 @@ var OpcPackage = class OpcPackage {
|
|
|
61
62
|
if (!relsBytes) throw new Error(`OPC package missing ${ROOT_RELS_PATH}`);
|
|
62
63
|
return new OpcPackage(parts, parseRelationships(relsBytes));
|
|
63
64
|
}
|
|
64
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* The bytes of the part at `path`, or undefined when absent.
|
|
67
|
+
*
|
|
68
|
+
* OPC part names are compared case-insensitively (ISO/IEC 29500-2 §9.1.1.1),
|
|
69
|
+
* so two names differing only in case are the SAME part. Producers do mix
|
|
70
|
+
* them: 123233_charts.xlsx writes `xl/worksheets/Sheet1.xml` and its
|
|
71
|
+
* relationships as `_rels/sheet1.xml.rels`, and an exact lookup found no
|
|
72
|
+
* relationships for that sheet at all — which cost it four charts, silently,
|
|
73
|
+
* because a sheet with no drawing rel is indistinguishable from one with no
|
|
74
|
+
* drawing. An exact hit still wins; the fold is only a fallback.
|
|
75
|
+
*/
|
|
65
76
|
getPart(path) {
|
|
66
|
-
|
|
77
|
+
const normalized = normalizePath(path);
|
|
78
|
+
return this.parts.get(normalized) ?? this.foldedParts().get(normalized.toLowerCase());
|
|
79
|
+
}
|
|
80
|
+
/** Parts keyed by their case-folded path, built once on first miss. */
|
|
81
|
+
foldedParts() {
|
|
82
|
+
if (!this.folded) {
|
|
83
|
+
const folded = /* @__PURE__ */ new Map();
|
|
84
|
+
for (const [path, data] of this.parts) {
|
|
85
|
+
const key = path.toLowerCase();
|
|
86
|
+
if (!folded.has(key)) folded.set(key, data);
|
|
87
|
+
}
|
|
88
|
+
this.folded = folded;
|
|
89
|
+
}
|
|
90
|
+
return this.folded;
|
|
67
91
|
}
|
|
68
92
|
/**
|
|
69
93
|
* The bytes of the part at `path`.
|
|
@@ -92,7 +116,7 @@ var OpcPackage = class OpcPackage {
|
|
|
92
116
|
const dir = slash >= 0 ? normalized.substring(0, slash) : "";
|
|
93
117
|
const base = slash >= 0 ? normalized.substring(slash + 1) : normalized;
|
|
94
118
|
const relsPath = dir.length > 0 ? `${dir}/_rels/${base}.rels` : `_rels/${base}.rels`;
|
|
95
|
-
const data = this.
|
|
119
|
+
const data = this.getPart(relsPath);
|
|
96
120
|
const rels = data ? parseRelationships(data) : [];
|
|
97
121
|
this.relsCache.set(normalized, rels);
|
|
98
122
|
return rels;
|
|
@@ -162,5 +186,20 @@ function collapseDotSegments(p) {
|
|
|
162
186
|
}
|
|
163
187
|
return out.join("/");
|
|
164
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* {@link unzipSync}, with a decompression failure turned into a named refusal.
|
|
191
|
+
*
|
|
192
|
+
* @param buffer The archive bytes.
|
|
193
|
+
* @param options The fflate options (the zip-bomb entry filter).
|
|
194
|
+
* @returns The decompressed entries.
|
|
195
|
+
* @throws Error naming the archive as invalid when inflation fails.
|
|
196
|
+
*/
|
|
197
|
+
function unzipGuarded(buffer, options) {
|
|
198
|
+
try {
|
|
199
|
+
return unzipSync(buffer, options);
|
|
200
|
+
} catch (e) {
|
|
201
|
+
throw new Error(`invalid zip data: ${e instanceof Error ? e.message : String(e)}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
165
204
|
//#endregion
|
|
166
205
|
export { OpcPackage };
|