@stll/folio-core 0.23.0 → 0.23.1
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/controller/layoutPipeline.js +7 -3
- package/dist/docx/blockContentParser.js +2 -47
- package/dist/docx/fieldParser.d.ts +1 -1
- package/dist/docx/fieldParser.js +12 -4
- package/dist/docx/numberingParser.d.ts +2 -11
- package/dist/docx/numberingParser.js +3 -79
- package/dist/docx/ooxmlCounterFormatter.d.ts +8 -0
- package/dist/docx/ooxmlCounterFormatter.js +134 -0
- package/dist/docx/serializer/tableSerializer.js +1 -1
- package/dist/docx/tableParser.js +2 -1
- package/dist/fields/bookmarkPages.js +1 -1
- package/dist/fields/evaluateField.js +1 -1
- package/dist/fields/fieldContext.d.ts +2 -0
- package/dist/fields/resolveFieldValues.js +5 -2
- package/dist/layout-bridge/convert/headerFooterLayout.js +6 -0
- package/dist/layout-bridge/convert/toFlowBlocks.js +16 -48
- package/dist/layout-engine/index.d.ts +3 -2
- package/dist/layout-engine/index.js +40 -16
- package/dist/layout-engine/measure/cache.js +1 -1
- package/dist/layout-engine/measure/complexScriptFormatting.d.ts +18 -0
- package/dist/layout-engine/measure/complexScriptFormatting.js +11 -0
- package/dist/layout-engine/measure/measureContainer.js +31 -16
- package/dist/layout-engine/measure/measureHelpers.js +4 -0
- package/dist/layout-engine/measure/measureParagraph.js +13 -3
- package/dist/layout-engine/measure/measureTypes.d.ts +4 -0
- package/dist/layout-engine/paginator.d.ts +5 -3
- package/dist/layout-engine/paginator.js +34 -13
- package/dist/layout-engine/types.d.ts +31 -4
- package/dist/layout-painter/index.js +2 -1
- package/dist/layout-painter/renderPage.js +9 -3
- package/dist/layout-painter/renderParagraph.js +42 -25
- package/dist/layout-painter/renderUtils.d.ts +2 -0
- package/dist/paged-layout/sectionGeometry.d.ts +3 -2
- package/dist/paged-layout/sectionGeometry.js +17 -1
- package/dist/prosemirror/attrs/index.js +5 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +4 -16
- package/dist/prosemirror/conversion/toProseDoc.js +4 -1
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.d.ts +3 -2
- package/dist/prosemirror/extensions/marks/RunFormattingOverrideExtension.js +31 -17
- package/dist/prosemirror/extensions/nodes/TableExtension.js +1 -0
- package/dist/prosemirror/schema/marks.d.ts +8 -0
- package/dist/prosemirror/schema/nodes.d.ts +2 -0
- package/package.json +1 -1
|
@@ -18,7 +18,7 @@ import "../layout-engine/types.js";
|
|
|
18
18
|
import { renderPages } from "../layout-painter/renderPage.js";
|
|
19
19
|
import { tryBuildIncrementalMeasures } from "../paged-layout/incrementalMeasure.js";
|
|
20
20
|
import { computePerBlockMeasureInputs } from "../paged-layout/sectionBlockWidths.js";
|
|
21
|
-
import { getMargins, getPageSize, twipsToPixels } from "../paged-layout/sectionGeometry.js";
|
|
21
|
+
import { getMargins, getPageNumbering, getPageSize, twipsToPixels } from "../paged-layout/sectionGeometry.js";
|
|
22
22
|
import { templatePreviewValuesKey } from "../prosemirror/plugins/templatePreviewValues.js";
|
|
23
23
|
import { getDocumentWatermark } from "../watermark/index.js";
|
|
24
24
|
//#region src/controller/layoutPipeline.ts
|
|
@@ -195,7 +195,8 @@ function runLayoutPipeline(deps, state, options = {}) {
|
|
|
195
195
|
phaseStartedAt = performance.now();
|
|
196
196
|
const bodyLayoutConfig = {
|
|
197
197
|
pageSize,
|
|
198
|
-
margins: initialBodyMargins
|
|
198
|
+
margins: initialBodyMargins,
|
|
199
|
+
pageNumbering: getPageNumbering(sectionProperties)
|
|
199
200
|
};
|
|
200
201
|
if (columns !== void 0) bodyLayoutConfig.columns = columns;
|
|
201
202
|
const finalSectionProperties = document?.package.document.sections?.at(-1)?.properties;
|
|
@@ -213,7 +214,8 @@ function runLayoutPipeline(deps, state, options = {}) {
|
|
|
213
214
|
authoredMargins: getMargins(finalSectionProperties),
|
|
214
215
|
preparedHeader: finalHeaderForLayout,
|
|
215
216
|
preparedFooter: finalFooterForLayout
|
|
216
|
-
})
|
|
217
|
+
}),
|
|
218
|
+
pageNumbering: getPageNumbering(finalSectionProperties)
|
|
217
219
|
} : bodyLayoutConfig;
|
|
218
220
|
const finalColumns = getColumns(finalSectionProperties);
|
|
219
221
|
if (finalColumns !== void 0) finalLayoutConfig.columns = finalColumns;
|
|
@@ -258,6 +260,7 @@ function runLayoutPipeline(deps, state, options = {}) {
|
|
|
258
260
|
const nextLayoutOpts = {
|
|
259
261
|
pageSize,
|
|
260
262
|
margins: initialBodyMargins,
|
|
263
|
+
pageNumbering: bodyLayoutConfig.pageNumbering,
|
|
261
264
|
pageGap,
|
|
262
265
|
mirrorMargins
|
|
263
266
|
};
|
|
@@ -273,6 +276,7 @@ function runLayoutPipeline(deps, state, options = {}) {
|
|
|
273
276
|
count: 1,
|
|
274
277
|
gap: 0
|
|
275
278
|
};
|
|
279
|
+
nextLayoutOpts.finalPageNumbering = finalLayoutConfig.pageNumbering;
|
|
276
280
|
}
|
|
277
281
|
if (columns !== void 0) nextLayoutOpts.columns = columns;
|
|
278
282
|
if (bodyBreakType !== void 0) nextLayoutOpts.bodyBreakType = bodyBreakType;
|
|
@@ -1,58 +1,13 @@
|
|
|
1
1
|
import { parseBookmarkEnd, parseBookmarkStart } from "./bookmarkParser.js";
|
|
2
2
|
import { appendBookmarkMarkerToLastParagraphInBlocks, prependBookmarkMarkersToFirstParagraphInBlocks } from "./bookmarkPlacement.js";
|
|
3
3
|
import { convertBulletToUnicode } from "./bulletMarkers.js";
|
|
4
|
-
import {
|
|
4
|
+
import { formatOoxmlCounter } from "./ooxmlCounterFormatter.js";
|
|
5
5
|
import { parseParagraph } from "./paragraphParser.js";
|
|
6
6
|
import { enrichParagraphTextBoxes } from "./paragraphTextBoxEnrichment.js";
|
|
7
7
|
import { parseSdtProperties } from "./sdtProperties.js";
|
|
8
8
|
import { parseTable } from "./tableParser.js";
|
|
9
9
|
import { elementToXml, findChild, getChildElements, getLocalName, mergeXmlnsDeclarations } from "./xmlParser.js";
|
|
10
10
|
//#region src/docx/blockContentParser.ts
|
|
11
|
-
const toRoman = (numParam) => {
|
|
12
|
-
let num = numParam;
|
|
13
|
-
const romanNumerals = [
|
|
14
|
-
[1e3, "M"],
|
|
15
|
-
[900, "CM"],
|
|
16
|
-
[500, "D"],
|
|
17
|
-
[400, "CD"],
|
|
18
|
-
[100, "C"],
|
|
19
|
-
[90, "XC"],
|
|
20
|
-
[50, "L"],
|
|
21
|
-
[40, "XL"],
|
|
22
|
-
[10, "X"],
|
|
23
|
-
[9, "IX"],
|
|
24
|
-
[5, "V"],
|
|
25
|
-
[4, "IV"],
|
|
26
|
-
[1, "I"]
|
|
27
|
-
];
|
|
28
|
-
let result = "";
|
|
29
|
-
for (const [value, symbol] of romanNumerals) while (num >= value) {
|
|
30
|
-
result += symbol;
|
|
31
|
-
num -= value;
|
|
32
|
-
}
|
|
33
|
-
return result;
|
|
34
|
-
};
|
|
35
|
-
const toRepeatedLetter = (value, baseCodePoint) => {
|
|
36
|
-
if (value <= 0) return "0";
|
|
37
|
-
const zeroBased = value - 1;
|
|
38
|
-
return String.fromCodePoint(baseCodePoint + zeroBased % 26).repeat(Math.floor(zeroBased / 26) + 1);
|
|
39
|
-
};
|
|
40
|
-
const formatNumber = (value, numFmt) => {
|
|
41
|
-
switch (numFmt) {
|
|
42
|
-
case "decimal": return String(value);
|
|
43
|
-
case "decimalZero": return padDecimal(value, 2);
|
|
44
|
-
case "decimalZero3": return padDecimal(value, 3);
|
|
45
|
-
case "decimalZero4": return padDecimal(value, 4);
|
|
46
|
-
case "decimalZero5": return padDecimal(value, 5);
|
|
47
|
-
case "lowerLetter": return toRepeatedLetter(value, 97);
|
|
48
|
-
case "upperLetter": return toRepeatedLetter(value, 65);
|
|
49
|
-
case "lowerRoman": return toRoman(value).toLowerCase();
|
|
50
|
-
case "upperRoman": return toRoman(value);
|
|
51
|
-
case "bullet": return "•";
|
|
52
|
-
case "none": return "";
|
|
53
|
-
default: return String(value);
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
11
|
const computeListMarker = (paragraph, { numbering, listCounters, abstractCounters, restartedNumIds, previousList, siblingNumIdsByAbstractNumId }) => {
|
|
57
12
|
const listRendering = paragraph.listRendering;
|
|
58
13
|
if (!listRendering || !numbering) {
|
|
@@ -137,7 +92,7 @@ const computeListMarker = (paragraph, { numbering, listCounters, abstractCounter
|
|
|
137
92
|
if (computedMarker.includes(placeholder)) {
|
|
138
93
|
const value = counters[lvl] ?? 0;
|
|
139
94
|
const levelInfo = numbering.getLevel(numId, lvl);
|
|
140
|
-
const formatted =
|
|
95
|
+
const formatted = formatOoxmlCounter(value, useLegalNumbering ? "decimal" : levelInfo?.numFmt || "decimal");
|
|
141
96
|
computedMarker = computedMarker.replaceAll(placeholder, formatted);
|
|
142
97
|
}
|
|
143
98
|
}
|
|
@@ -188,7 +188,7 @@ declare function isTocField(field: document_d_exports.Field): boolean;
|
|
|
188
188
|
* @param instruction - Parsed instruction for format switches
|
|
189
189
|
* @returns Formatted page number string
|
|
190
190
|
*/
|
|
191
|
-
declare function computePageNumber(pageNumber: number, instruction?: ParsedFieldInstruction): string;
|
|
191
|
+
declare function computePageNumber(pageNumber: number, instruction?: ParsedFieldInstruction, sectionFormat?: string): string;
|
|
192
192
|
/**
|
|
193
193
|
* Format a date according to a format string
|
|
194
194
|
*
|
package/dist/docx/fieldParser.js
CHANGED
|
@@ -345,16 +345,24 @@ function isTocField(field) {
|
|
|
345
345
|
* @param instruction - Parsed instruction for format switches
|
|
346
346
|
* @returns Formatted page number string
|
|
347
347
|
*/
|
|
348
|
-
function computePageNumber(pageNumber, instruction) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if (!format) return String(pageNumber);
|
|
348
|
+
function computePageNumber(pageNumber, instruction, sectionFormat) {
|
|
349
|
+
const format = instruction ? getFormatSwitch(instruction) : void 0;
|
|
350
|
+
if (!format || format.toUpperCase() === "MERGEFORMAT") return formatSectionPageNumber(pageNumber, sectionFormat);
|
|
352
351
|
switch (format.toUpperCase()) {
|
|
353
352
|
case "ROMAN": return toRoman(pageNumber);
|
|
354
353
|
case "ALPHABETIC": return toLetter(pageNumber);
|
|
355
354
|
default: return String(pageNumber);
|
|
356
355
|
}
|
|
357
356
|
}
|
|
357
|
+
function formatSectionPageNumber(pageNumber, format) {
|
|
358
|
+
switch (format) {
|
|
359
|
+
case "upperRoman": return toRoman(pageNumber);
|
|
360
|
+
case "lowerRoman": return toRoman(pageNumber).toLowerCase();
|
|
361
|
+
case "upperLetter": return toLetter(pageNumber);
|
|
362
|
+
case "lowerLetter": return toLetter(pageNumber).toLowerCase();
|
|
363
|
+
default: return String(pageNumber);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
358
366
|
/**
|
|
359
367
|
* Convert number to uppercase Roman numerals
|
|
360
368
|
*/
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { formatOoxmlCounter, padDecimal } from "./ooxmlCounterFormatter.js";
|
|
2
3
|
//#region src/docx/numberingParser.d.ts
|
|
3
4
|
/**
|
|
4
5
|
* Map of rId to numbering definitions
|
|
@@ -46,16 +47,6 @@ declare function computeListRendering(numPr: {
|
|
|
46
47
|
}, numbering: NumberingMap): (document_d_exports.ListRendering & {
|
|
47
48
|
levelStarts: number[];
|
|
48
49
|
}) | null;
|
|
49
|
-
/**
|
|
50
|
-
* Format a number according to the specified format
|
|
51
|
-
*
|
|
52
|
-
* @param num - The number to format
|
|
53
|
-
* @param format - The number format
|
|
54
|
-
* @returns Formatted string
|
|
55
|
-
*/
|
|
56
|
-
declare function formatNumber(num: number, format: document_d_exports.NumberFormat): string;
|
|
57
|
-
/** Zero-pad a counter to `width` digits ("decimalZero" family, §17.18.59). */
|
|
58
|
-
declare function padDecimal(num: number, width: number): string;
|
|
59
50
|
/**
|
|
60
51
|
* Compare two `numPr` references by value — `numId` plus `ilvl` (a missing
|
|
61
52
|
* `ilvl` is level 0). Used for the style-sourced-numbering provenance check;
|
|
@@ -89,4 +80,4 @@ declare function getBulletCharacter(level: document_d_exports.ListLevel): string
|
|
|
89
80
|
*/
|
|
90
81
|
declare function isBulletLevel(level: document_d_exports.ListLevel): boolean;
|
|
91
82
|
//#endregion
|
|
92
|
-
export { NumberingMap, computeListRendering, createNumberingMap, formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, numPrEqual, padDecimal, parseNumbering, renderListMarker };
|
|
83
|
+
export { NumberingMap, computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, numPrEqual, padDecimal, parseNumbering, renderListMarker };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatOoxmlCounter, padDecimal } from "./ooxmlCounterFormatter.js";
|
|
1
2
|
import { FontHintSchema, LevelSuffixSchema, ThemeColorSlotSchema, narrowEnum } from "./parserEnums.js";
|
|
2
3
|
import { findChild, findChildren, getAttribute, parseBooleanElement, parseNumericAttribute, parseXmlDocument } from "./xmlParser.js";
|
|
3
4
|
//#region src/docx/numberingParser.ts
|
|
@@ -614,37 +615,6 @@ function computeListRendering(numPr, numbering) {
|
|
|
614
615
|
return rendering;
|
|
615
616
|
}
|
|
616
617
|
/**
|
|
617
|
-
* Format a number according to the specified format
|
|
618
|
-
*
|
|
619
|
-
* @param num - The number to format
|
|
620
|
-
* @param format - The number format
|
|
621
|
-
* @returns Formatted string
|
|
622
|
-
*/
|
|
623
|
-
function formatNumber(num, format) {
|
|
624
|
-
switch (format) {
|
|
625
|
-
case "decimal": return num.toString();
|
|
626
|
-
case "decimalZero": return padDecimal(num, 2);
|
|
627
|
-
case "decimalZero3": return padDecimal(num, 3);
|
|
628
|
-
case "decimalZero4": return padDecimal(num, 4);
|
|
629
|
-
case "decimalZero5": return padDecimal(num, 5);
|
|
630
|
-
case "upperRoman": return toRoman(num).toUpperCase();
|
|
631
|
-
case "lowerRoman": return toRoman(num).toLowerCase();
|
|
632
|
-
case "upperLetter": return toLetter(num).toUpperCase();
|
|
633
|
-
case "lowerLetter": return toLetter(num).toLowerCase();
|
|
634
|
-
case "ordinal": return toOrdinal(num);
|
|
635
|
-
case "bullet": return "•";
|
|
636
|
-
case "none": return "";
|
|
637
|
-
case "decimalEnclosedParen": return `(${num})`;
|
|
638
|
-
case "numberInDash": return `-${num}-`;
|
|
639
|
-
default: return num.toString();
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
/** Zero-pad a counter to `width` digits ("decimalZero" family, §17.18.59). */
|
|
643
|
-
function padDecimal(num, width) {
|
|
644
|
-
if (num < 0) return num.toString();
|
|
645
|
-
return num.toString().padStart(width, "0");
|
|
646
|
-
}
|
|
647
|
-
/**
|
|
648
618
|
* Compare two `numPr` references by value — `numId` plus `ilvl` (a missing
|
|
649
619
|
* `ilvl` is level 0). Used for the style-sourced-numbering provenance check;
|
|
650
620
|
* avoids JSON.stringify equality, which is sensitive to key order.
|
|
@@ -654,52 +624,6 @@ function numPrEqual(a, b) {
|
|
|
654
624
|
return a.numId === b.numId && (a.ilvl ?? 0) === (b.ilvl ?? 0);
|
|
655
625
|
}
|
|
656
626
|
/**
|
|
657
|
-
* Convert number to Roman numerals
|
|
658
|
-
*/
|
|
659
|
-
function toRoman(num) {
|
|
660
|
-
if (num <= 0 || num > 3999) return num.toString();
|
|
661
|
-
const romanNumerals = [
|
|
662
|
-
[1e3, "m"],
|
|
663
|
-
[900, "cm"],
|
|
664
|
-
[500, "d"],
|
|
665
|
-
[400, "cd"],
|
|
666
|
-
[100, "c"],
|
|
667
|
-
[90, "xc"],
|
|
668
|
-
[50, "l"],
|
|
669
|
-
[40, "xl"],
|
|
670
|
-
[10, "x"],
|
|
671
|
-
[9, "ix"],
|
|
672
|
-
[5, "v"],
|
|
673
|
-
[4, "iv"],
|
|
674
|
-
[1, "i"]
|
|
675
|
-
];
|
|
676
|
-
let result = "";
|
|
677
|
-
let remaining = num;
|
|
678
|
-
for (const [value, numeral] of romanNumerals) while (remaining >= value) {
|
|
679
|
-
result += numeral;
|
|
680
|
-
remaining -= value;
|
|
681
|
-
}
|
|
682
|
-
return result;
|
|
683
|
-
}
|
|
684
|
-
function toLetter(num) {
|
|
685
|
-
if (num <= 0) return "";
|
|
686
|
-
const zeroBased = num - 1;
|
|
687
|
-
return String.fromCodePoint(97 + zeroBased % 26).repeat(Math.floor(zeroBased / 26) + 1);
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* Convert number to ordinal (1st, 2nd, 3rd, ...)
|
|
691
|
-
*/
|
|
692
|
-
function toOrdinal(num) {
|
|
693
|
-
const suffix = [
|
|
694
|
-
"th",
|
|
695
|
-
"st",
|
|
696
|
-
"nd",
|
|
697
|
-
"rd"
|
|
698
|
-
];
|
|
699
|
-
const v = num % 100;
|
|
700
|
-
return num + (suffix[(v - 20) % 10] ?? suffix[v] ?? suffix[0] ?? "th");
|
|
701
|
-
}
|
|
702
|
-
/**
|
|
703
627
|
* Render list marker text by replacing placeholders with formatted numbers
|
|
704
628
|
*
|
|
705
629
|
* @param lvlText - The level text pattern (e.g., "%1.", "%1.%2")
|
|
@@ -713,7 +637,7 @@ function renderListMarker(lvlText, counters, formats) {
|
|
|
713
637
|
const placeholder = `%${i}`;
|
|
714
638
|
if (result.includes(placeholder)) {
|
|
715
639
|
const counterIndex = i - 1;
|
|
716
|
-
const formatted =
|
|
640
|
+
const formatted = formatOoxmlCounter(counters[counterIndex] ?? 1, formats[counterIndex] ?? "decimal");
|
|
717
641
|
result = result.replaceAll(placeholder, formatted);
|
|
718
642
|
}
|
|
719
643
|
}
|
|
@@ -742,4 +666,4 @@ function isBulletLevel(level) {
|
|
|
742
666
|
return level.numFmt === "bullet" || level.numFmt === "none";
|
|
743
667
|
}
|
|
744
668
|
//#endregion
|
|
745
|
-
export { computeListRendering, createNumberingMap, formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, numPrEqual, padDecimal, parseNumbering, renderListMarker };
|
|
669
|
+
export { computeListRendering, createNumberingMap, formatOoxmlCounter as formatNumber, getBulletCharacter, getCachedNumberingMap, isBulletLevel, numPrEqual, padDecimal, parseNumbering, renderListMarker };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
//#region src/docx/ooxmlCounterFormatter.d.ts
|
|
3
|
+
/** Zero-pad a counter to `width` digits (the `decimalZero` family). */
|
|
4
|
+
declare const padDecimal: (value: number, width: number) => string;
|
|
5
|
+
/** Format one OOXML numbering counter using Microsoft Word display semantics. */
|
|
6
|
+
declare const formatOoxmlCounter: (value: number, format: document_d_exports.NumberFormat | undefined) => string;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { formatOoxmlCounter, padDecimal };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
//#region src/docx/ooxmlCounterFormatter.ts
|
|
2
|
+
const ROMAN_PAIRS = [
|
|
3
|
+
[1e3, "M"],
|
|
4
|
+
[900, "CM"],
|
|
5
|
+
[500, "D"],
|
|
6
|
+
[400, "CD"],
|
|
7
|
+
[100, "C"],
|
|
8
|
+
[90, "XC"],
|
|
9
|
+
[50, "L"],
|
|
10
|
+
[40, "XL"],
|
|
11
|
+
[10, "X"],
|
|
12
|
+
[9, "IX"],
|
|
13
|
+
[5, "V"],
|
|
14
|
+
[4, "IV"],
|
|
15
|
+
[1, "I"]
|
|
16
|
+
];
|
|
17
|
+
const LATIN_LOWER_SEQUENCE = [..."abcdefghijklmnopqrstuvwxyz"];
|
|
18
|
+
const ARABIC_ALPHA_SEQUENCE = [..."أبتثجحخدذرزسشصضطظعغفقكلمنهوي"];
|
|
19
|
+
const ARABIC_ABJAD_SEQUENCE = [..."أبجدهوزحطيكلمنسعفصقرشتثخذضظغ"];
|
|
20
|
+
const ZERO_WIDTH_NON_JOINER = "";
|
|
21
|
+
const MAX_REPEATED_COUNTER_LENGTH = 1024;
|
|
22
|
+
const toRoman = (value) => {
|
|
23
|
+
if (value <= 0 || value > 3999) return String(value);
|
|
24
|
+
let remaining = value;
|
|
25
|
+
let result = "";
|
|
26
|
+
for (const [number, numeral] of ROMAN_PAIRS) while (remaining >= number) {
|
|
27
|
+
result += numeral;
|
|
28
|
+
remaining -= number;
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
const toRepeatedSequence = (value, sequence) => {
|
|
33
|
+
if (!Number.isSafeInteger(value) || value <= 0) return "";
|
|
34
|
+
const zeroBased = value - 1;
|
|
35
|
+
const character = sequence.at(zeroBased % sequence.length);
|
|
36
|
+
const repetitions = Math.floor(zeroBased / sequence.length) + 1;
|
|
37
|
+
if (repetitions > MAX_REPEATED_COUNTER_LENGTH) return;
|
|
38
|
+
return character?.repeat(repetitions) ?? "";
|
|
39
|
+
};
|
|
40
|
+
const toOrdinal = (value) => {
|
|
41
|
+
const suffixes = [
|
|
42
|
+
"th",
|
|
43
|
+
"st",
|
|
44
|
+
"nd",
|
|
45
|
+
"rd"
|
|
46
|
+
];
|
|
47
|
+
const remainder = value % 100;
|
|
48
|
+
return `${value}${suffixes[(remainder - 20) % 10] ?? suffixes[remainder] ?? "th"}`;
|
|
49
|
+
};
|
|
50
|
+
/** Zero-pad a counter to `width` digits (the `decimalZero` family). */
|
|
51
|
+
const padDecimal = (value, width) => {
|
|
52
|
+
if (value < 0) return String(value);
|
|
53
|
+
return String(value).padStart(width, "0");
|
|
54
|
+
};
|
|
55
|
+
/** Format one OOXML numbering counter using Microsoft Word display semantics. */
|
|
56
|
+
const formatOoxmlCounter = (value, format) => {
|
|
57
|
+
if (!Number.isFinite(value)) return "";
|
|
58
|
+
switch (format ?? "decimal") {
|
|
59
|
+
case "decimal":
|
|
60
|
+
case "cardinalText":
|
|
61
|
+
case "ordinalText":
|
|
62
|
+
case "hex":
|
|
63
|
+
case "chicago":
|
|
64
|
+
case "ideographDigital":
|
|
65
|
+
case "japaneseCounting":
|
|
66
|
+
case "aiueo":
|
|
67
|
+
case "iroha":
|
|
68
|
+
case "decimalFullWidth":
|
|
69
|
+
case "decimalHalfWidth":
|
|
70
|
+
case "japaneseLegal":
|
|
71
|
+
case "japaneseDigitalTenThousand":
|
|
72
|
+
case "decimalEnclosedCircle":
|
|
73
|
+
case "decimalFullWidth2":
|
|
74
|
+
case "aiueoFullWidth":
|
|
75
|
+
case "irohaFullWidth":
|
|
76
|
+
case "ganada":
|
|
77
|
+
case "chosung":
|
|
78
|
+
case "decimalEnclosedFullstop":
|
|
79
|
+
case "decimalEnclosedCircleChinese":
|
|
80
|
+
case "ideographEnclosedCircle":
|
|
81
|
+
case "ideographTraditional":
|
|
82
|
+
case "ideographZodiac":
|
|
83
|
+
case "ideographZodiacTraditional":
|
|
84
|
+
case "taiwaneseCounting":
|
|
85
|
+
case "ideographLegalTraditional":
|
|
86
|
+
case "taiwaneseCountingThousand":
|
|
87
|
+
case "taiwaneseDigital":
|
|
88
|
+
case "chineseCounting":
|
|
89
|
+
case "chineseLegalSimplified":
|
|
90
|
+
case "chineseCountingThousand":
|
|
91
|
+
case "koreanDigital":
|
|
92
|
+
case "koreanCounting":
|
|
93
|
+
case "koreanLegal":
|
|
94
|
+
case "koreanDigital2":
|
|
95
|
+
case "vietnameseCounting":
|
|
96
|
+
case "russianLower":
|
|
97
|
+
case "russianUpper":
|
|
98
|
+
case "hebrew1":
|
|
99
|
+
case "hebrew2":
|
|
100
|
+
case "hindiVowels":
|
|
101
|
+
case "hindiConsonants":
|
|
102
|
+
case "hindiNumbers":
|
|
103
|
+
case "hindiCounting":
|
|
104
|
+
case "thaiLetters":
|
|
105
|
+
case "thaiNumbers":
|
|
106
|
+
case "thaiCounting": return String(value);
|
|
107
|
+
case "decimalZero": return padDecimal(value, 2);
|
|
108
|
+
case "decimalZero3": return padDecimal(value, 3);
|
|
109
|
+
case "decimalZero4": return padDecimal(value, 4);
|
|
110
|
+
case "decimalZero5": return padDecimal(value, 5);
|
|
111
|
+
case "upperRoman": return toRoman(value);
|
|
112
|
+
case "lowerRoman": return toRoman(value).toLowerCase();
|
|
113
|
+
case "upperLetter": return (toRepeatedSequence(value, LATIN_LOWER_SEQUENCE) ?? String(value)).toUpperCase();
|
|
114
|
+
case "lowerLetter": return toRepeatedSequence(value, LATIN_LOWER_SEQUENCE) ?? String(value);
|
|
115
|
+
case "ordinal": return toOrdinal(value);
|
|
116
|
+
case "bullet": return "•";
|
|
117
|
+
case "none": return "";
|
|
118
|
+
case "decimalEnclosedParen": return `(${value})`;
|
|
119
|
+
case "numberInDash": return `-${value}-`;
|
|
120
|
+
case "arabicAlpha": {
|
|
121
|
+
const counter = toRepeatedSequence(value, ARABIC_ALPHA_SEQUENCE);
|
|
122
|
+
if (counter === void 0) return String(value);
|
|
123
|
+
return counter ? `${counter}${ZERO_WIDTH_NON_JOINER}` : "";
|
|
124
|
+
}
|
|
125
|
+
case "arabicAbjad": {
|
|
126
|
+
const counter = toRepeatedSequence(value, ARABIC_ABJAD_SEQUENCE);
|
|
127
|
+
if (counter === void 0) return String(value);
|
|
128
|
+
return counter ? `${ZERO_WIDTH_NON_JOINER}${counter}` : "";
|
|
129
|
+
}
|
|
130
|
+
default: return String(value);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
//#endregion
|
|
134
|
+
export { formatOoxmlCounter, padDecimal };
|
|
@@ -134,7 +134,7 @@ function serializeTableFormatting(formatting, propertyChanges) {
|
|
|
134
134
|
if (formatting.styleId) parts.push(`<w:tblStyle w:val="${escapeXml(formatting.styleId)}"/>`);
|
|
135
135
|
const floatingXml = serializeFloatingTableProperties(formatting.floating);
|
|
136
136
|
if (floatingXml) parts.push(floatingXml);
|
|
137
|
-
if (formatting.bidi) parts.push("<w:bidiVisual/>");
|
|
137
|
+
if (formatting.bidi !== void 0) parts.push(formatting.bidi ? "<w:bidiVisual/>" : "<w:bidiVisual w:val=\"0\"/>");
|
|
138
138
|
const widthXml = serializeMeasurement(formatting.width, "tblW");
|
|
139
139
|
if (widthXml) parts.push(widthXml);
|
|
140
140
|
if (formatting.justification) parts.push(`<w:jc w:val="${formatting.justification}"/>`);
|
package/dist/docx/tableParser.js
CHANGED
|
@@ -284,7 +284,8 @@ function parseTableProperties(tblPrElement) {
|
|
|
284
284
|
}
|
|
285
285
|
const floating = parseFloatingTableProperties(findChild(tblPrElement, "w", "tblpPr"));
|
|
286
286
|
if (floating) formatting.floating = floating;
|
|
287
|
-
|
|
287
|
+
const bidiVisual = findChild(tblPrElement, "w", "bidiVisual");
|
|
288
|
+
if (bidiVisual) formatting.bidi = parseBooleanElement(bidiVisual);
|
|
288
289
|
if (Object.keys(formatting).length === 0) return;
|
|
289
290
|
return formatting;
|
|
290
291
|
}
|
|
@@ -13,7 +13,7 @@ function buildBookmarkPageMap(pages, blocks) {
|
|
|
13
13
|
if (anchors.length === 0) return pageByBookmark;
|
|
14
14
|
for (const page of pages) for (const fragment of page.fragments) for (const anchor of anchors) {
|
|
15
15
|
if (!fragmentContainsAnchor(fragment, anchor)) continue;
|
|
16
|
-
assignBookmarkPage(pageByBookmark, anchor.names, page.
|
|
16
|
+
assignBookmarkPage(pageByBookmark, anchor.names, page.logicalNumber);
|
|
17
17
|
}
|
|
18
18
|
return pageByBookmark;
|
|
19
19
|
}
|
|
@@ -38,7 +38,7 @@ function evaluateField(parsed, ctx, options = {}) {
|
|
|
38
38
|
const fallback = options.fallback ?? "";
|
|
39
39
|
if (options.locked) return fallback;
|
|
40
40
|
switch (parsed.type) {
|
|
41
|
-
case "PAGE": return computePageNumber(ctx.pageNumber, parsed);
|
|
41
|
+
case "PAGE": return computePageNumber(ctx.pageNumber, parsed, ctx.pageNumberFormat);
|
|
42
42
|
case "NUMPAGES": return computePageNumber(ctx.totalPages, parsed);
|
|
43
43
|
case "SECTIONPAGES": return ctx.sectionPages === void 0 ? fallback : computePageNumber(ctx.sectionPages, parsed);
|
|
44
44
|
case "TIME": {
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
type FieldContext = {
|
|
10
10
|
/** 1-indexed page the field is on (PAGE). */
|
|
11
11
|
pageNumber: number;
|
|
12
|
+
/** OOXML section-level format used when PAGE has no explicit format switch. */
|
|
13
|
+
pageNumberFormat?: string;
|
|
12
14
|
/** Total pages in the document (NUMPAGES). */
|
|
13
15
|
totalPages: number;
|
|
14
16
|
/** Pages in the field's current section (SECTIONPAGES); omit until the
|
|
@@ -22,6 +22,7 @@ function resolveFieldValues(blocks, pages, shared) {
|
|
|
22
22
|
const sectionPages = location ? shared.sectionPageCounts.get(location.sectionIndex) : void 0;
|
|
23
23
|
const context = {
|
|
24
24
|
pageNumber: location?.page ?? 1,
|
|
25
|
+
...location?.pageFormat === void 0 ? {} : { pageNumberFormat: location.pageFormat },
|
|
25
26
|
totalPages: shared.totalPages,
|
|
26
27
|
bookmarkPages: shared.bookmarkPages,
|
|
27
28
|
bookmarkText: shared.bookmarkText,
|
|
@@ -131,7 +132,8 @@ function fieldValuesEqual(a, b) {
|
|
|
131
132
|
function buildBlockPageMap(pages) {
|
|
132
133
|
const map = /* @__PURE__ */ new Map();
|
|
133
134
|
for (const page of pages) for (const fragment of page.fragments) if (!map.has(fragment.blockId)) map.set(fragment.blockId, {
|
|
134
|
-
page: page.
|
|
135
|
+
page: page.logicalNumber,
|
|
136
|
+
...page.logicalNumberFormat === void 0 ? {} : { pageFormat: page.logicalNumberFormat },
|
|
135
137
|
sectionIndex: page.sectionIndex ?? 0
|
|
136
138
|
});
|
|
137
139
|
return map;
|
|
@@ -143,7 +145,8 @@ function buildTableRowPageMap(pages) {
|
|
|
143
145
|
for (let rowIndex = fragment.fromRow; rowIndex < fragment.toRow; rowIndex++) {
|
|
144
146
|
const key = tableRowKey(fragment.blockId, rowIndex);
|
|
145
147
|
if (!map.has(key)) map.set(key, {
|
|
146
|
-
page: page.
|
|
148
|
+
page: page.logicalNumber,
|
|
149
|
+
...page.logicalNumberFormat === void 0 ? {} : { pageFormat: page.logicalNumberFormat },
|
|
147
150
|
sectionIndex: page.sectionIndex ?? 0
|
|
148
151
|
});
|
|
149
152
|
}
|
|
@@ -470,6 +470,12 @@ function serializeRunFmt(run) {
|
|
|
470
470
|
"highlightColor",
|
|
471
471
|
"fontSize",
|
|
472
472
|
"fontFamily",
|
|
473
|
+
"eastAsiaFontFamily",
|
|
474
|
+
"complexScriptFontFamily",
|
|
475
|
+
"complexScriptFontSize",
|
|
476
|
+
"complexScriptBold",
|
|
477
|
+
"complexScriptItalic",
|
|
478
|
+
"forceComplexScript",
|
|
473
479
|
"verticalAlign",
|
|
474
480
|
"letterSpacing",
|
|
475
481
|
"smallCaps",
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { convertBulletToUnicode } from "../../docx/bulletMarkers.js";
|
|
2
2
|
import { resolveDocumentGridLinePitch } from "../../docx/documentGrid.js";
|
|
3
|
-
import {
|
|
3
|
+
import { formatOoxmlCounter } from "../../docx/ooxmlCounterFormatter.js";
|
|
4
4
|
import { setParagraphFrame } from "../../layout-engine/paragraphFrame.js";
|
|
5
5
|
import { setTextBoxGroupId } from "../../layout-engine/textBoxGroup.js";
|
|
6
6
|
import { DEFAULT_TEXTBOX_MARGINS } from "../../layout-engine/types.js";
|
|
7
|
+
import { getPageNumbering } from "../../paged-layout/sectionGeometry.js";
|
|
7
8
|
import { expectBlockSdtAttrs, expectCharacterSpacingMarkAttrs, expectCommentMarkAttrs, expectEmphasisMarkAttrs, expectFieldAttrs, expectFontFamilyMarkAttrs, expectFontSizeMarkAttrs, expectFootnoteRefMarkAttrs, expectHardBreakAttrs, expectHighlightMarkAttrs, expectHyperlinkMarkAttrs, expectImageAttrs, expectLanguageMarkAttrs, expectMathAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs, expectRunShadingMarkAttrs, expectSymbolAttrs, expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, expectTextBoxAttrs, expectTextColorMarkAttrs, expectTextEffectMarkAttrs, expectTrackedChangeMarkAttrs, expectUnderlineMarkAttrs } from "../../prosemirror/attrs/index.js";
|
|
8
9
|
import { autospacingMatchesBase } from "../../prosemirror/autospacingBase.js";
|
|
9
10
|
import { runShadingAttrsToShading } from "../../prosemirror/conversion/runShadingMark.js";
|
|
10
|
-
import {
|
|
11
|
+
import { directionToBidi } from "../../prosemirror/paragraphDirection.js";
|
|
11
12
|
import { assertValidProseMirrorDocument } from "../../prosemirror/validation.js";
|
|
12
13
|
import { resolveColor, resolveHighlightToCss } from "../../utils/colorResolver.js";
|
|
13
14
|
import { resolveThemeFont } from "../../utils/fontResolver.js";
|
|
@@ -74,51 +75,8 @@ function formatNumberedMarker(counters, level) {
|
|
|
74
75
|
if (parts.length === 0) return "1.";
|
|
75
76
|
return `${parts.join(".")}.`;
|
|
76
77
|
}
|
|
77
|
-
const ROMAN_PAIRS = [
|
|
78
|
-
[1e3, "M"],
|
|
79
|
-
[900, "CM"],
|
|
80
|
-
[500, "D"],
|
|
81
|
-
[400, "CD"],
|
|
82
|
-
[100, "C"],
|
|
83
|
-
[90, "XC"],
|
|
84
|
-
[50, "L"],
|
|
85
|
-
[40, "XL"],
|
|
86
|
-
[10, "X"],
|
|
87
|
-
[9, "IX"],
|
|
88
|
-
[5, "V"],
|
|
89
|
-
[4, "IV"],
|
|
90
|
-
[1, "I"]
|
|
91
|
-
];
|
|
92
|
-
function toRoman(value, upper) {
|
|
93
|
-
if (value <= 0) return "";
|
|
94
|
-
let remaining = value;
|
|
95
|
-
let output = "";
|
|
96
|
-
for (const [number, symbol] of ROMAN_PAIRS) while (remaining >= number) {
|
|
97
|
-
output += symbol;
|
|
98
|
-
remaining -= number;
|
|
99
|
-
}
|
|
100
|
-
return upper ? output : output.toLowerCase();
|
|
101
|
-
}
|
|
102
|
-
function toLetter(value, upper) {
|
|
103
|
-
if (value <= 0) return "";
|
|
104
|
-
const zeroBased = value - 1;
|
|
105
|
-
const baseCodePoint = upper ? 65 : 97;
|
|
106
|
-
return String.fromCodePoint(baseCodePoint + zeroBased % 26).repeat(Math.floor(zeroBased / 26) + 1);
|
|
107
|
-
}
|
|
108
78
|
function formatCounter(value, format) {
|
|
109
|
-
|
|
110
|
-
switch (format) {
|
|
111
|
-
case "upperRoman": return toRoman(value, true);
|
|
112
|
-
case "lowerRoman": return toRoman(value, false);
|
|
113
|
-
case "upperLetter": return toLetter(value, true);
|
|
114
|
-
case "lowerLetter": return toLetter(value, false);
|
|
115
|
-
case "decimalZero": return padDecimal(value, 2);
|
|
116
|
-
case "decimalZero3": return padDecimal(value, 3);
|
|
117
|
-
case "decimalZero4": return padDecimal(value, 4);
|
|
118
|
-
case "decimalZero5": return padDecimal(value, 5);
|
|
119
|
-
case "none": return "";
|
|
120
|
-
default: return String(value);
|
|
121
|
-
}
|
|
79
|
+
return formatOoxmlCounter(value, format);
|
|
122
80
|
}
|
|
123
81
|
function resolveListTemplate(template, counters, levelFormats, forceDecimal = false) {
|
|
124
82
|
return template.replace(/%(?<digit>\d)(?<punct>[.):\]])?/gu, (...args) => {
|
|
@@ -399,6 +357,10 @@ function applyRunFormattingOverrides(formatting, attrs) {
|
|
|
399
357
|
if (attrs.shadow === false) formatting.textShadow = false;
|
|
400
358
|
if (attrs.outline === false) formatting.textOutline = false;
|
|
401
359
|
if (attrs.rtl === false) formatting.rtl = false;
|
|
360
|
+
if (attrs.boldCs !== void 0) formatting.complexScriptBold = attrs.boldCs;
|
|
361
|
+
if (attrs.italicCs !== void 0) formatting.complexScriptItalic = attrs.italicCs;
|
|
362
|
+
if (attrs.fontSizeCs !== void 0) formatting.complexScriptFontSize = attrs.fontSizeCs / 2;
|
|
363
|
+
if (attrs.cs !== void 0) formatting.forceComplexScript = attrs.cs;
|
|
402
364
|
}
|
|
403
365
|
function paragraphRunDefaults(pmAttrs, theme) {
|
|
404
366
|
const defaultTextFormatting = pmAttrs.defaultTextFormatting;
|
|
@@ -412,8 +374,12 @@ function paragraphRunDefaults(pmAttrs, theme) {
|
|
|
412
374
|
if (complexScriptFontFamily) result.complexScriptFontFamily = complexScriptFontFamily;
|
|
413
375
|
if (defaultTextFormatting.language) result.language = { ...defaultTextFormatting.language };
|
|
414
376
|
if (defaultTextFormatting.fontSize !== void 0) result.fontSize = defaultTextFormatting.fontSize / 2;
|
|
377
|
+
if (defaultTextFormatting.fontSizeCs !== void 0) result.complexScriptFontSize = defaultTextFormatting.fontSizeCs / 2;
|
|
415
378
|
if (defaultTextFormatting.bold !== void 0) result.bold = defaultTextFormatting.bold;
|
|
379
|
+
if (defaultTextFormatting.boldCs !== void 0) result.complexScriptBold = defaultTextFormatting.boldCs;
|
|
416
380
|
if (defaultTextFormatting.italic !== void 0) result.italic = defaultTextFormatting.italic;
|
|
381
|
+
if (defaultTextFormatting.italicCs !== void 0) result.complexScriptItalic = defaultTextFormatting.italicCs;
|
|
382
|
+
if (defaultTextFormatting.cs !== void 0) result.forceComplexScript = defaultTextFormatting.cs;
|
|
417
383
|
if (defaultTextFormatting.underline && defaultTextFormatting.underline.style !== "none") {
|
|
418
384
|
result.underline = { style: defaultTextFormatting.underline.style };
|
|
419
385
|
if (defaultTextFormatting.underline.color) result.underline.color = resolveColor(defaultTextFormatting.underline.color, theme);
|
|
@@ -821,7 +787,8 @@ function convertParagraphAttrs(pmAttrs, theme, listCounters, listAbstractCounter
|
|
|
821
787
|
if (pmAttrs.widowControl === false) attrs.widowControl = false;
|
|
822
788
|
if (pmAttrs.contextualSpacing) attrs.contextualSpacing = true;
|
|
823
789
|
if (pmAttrs.runInWithNext) attrs.runInWithNext = true;
|
|
824
|
-
|
|
790
|
+
const bidi = directionToBidi(pmAttrs.direction);
|
|
791
|
+
if (bidi !== void 0) attrs.bidi = bidi;
|
|
825
792
|
if (pmAttrs.styleId) attrs.styleId = pmAttrs.styleId;
|
|
826
793
|
const propertyChanges = pmAttrs._propertyChanges ?? [];
|
|
827
794
|
let changedNumberingChange;
|
|
@@ -1187,7 +1154,7 @@ function convertTable(node, startPos, options) {
|
|
|
1187
1154
|
if (justification) tableBlock.justification = justification;
|
|
1188
1155
|
if (indentPx !== void 0) tableBlock.indent = indentPx;
|
|
1189
1156
|
if (floatingPx) tableBlock.floating = floatingPx;
|
|
1190
|
-
if (originalFormatting?.bidi) tableBlock.bidi = true;
|
|
1157
|
+
if (attrs._resolvedBidi ?? originalFormatting?.bidi) tableBlock.bidi = true;
|
|
1191
1158
|
return tableBlock;
|
|
1192
1159
|
}
|
|
1193
1160
|
/**
|
|
@@ -1462,6 +1429,7 @@ function toFlowBlocks(doc, options = {}) {
|
|
|
1462
1429
|
const breakType = secProps?.sectionStart ?? pmAttrs.sectionBreakType;
|
|
1463
1430
|
if (breakType) sectionBreak.type = breakType;
|
|
1464
1431
|
if (secProps) {
|
|
1432
|
+
sectionBreak.pageNumbering = getPageNumbering(secProps);
|
|
1465
1433
|
const documentGridLinePitchTwips = resolveDocumentGridLinePitch(secProps.docGrid);
|
|
1466
1434
|
if (documentGridLinePitchTwips !== void 0) sectionBreak.documentGridLinePitchTwips = documentGridLinePitchTwips;
|
|
1467
1435
|
if (secProps.pageWidth || secProps.pageHeight) sectionBreak.pageSize = {
|