reamkit 1.33.1 → 1.34.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 +13 -10
- package/dist/esm/core/converter/facade.js +1 -0
- package/dist/esm/core/converter/ream.d.ts +6 -6
- package/dist/esm/core/converter/ream.js +11 -5
- package/dist/esm/core/crypto/index.js +1 -2
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +15 -0
- package/dist/esm/core/drawingml/text-warp.d.ts +34 -0
- package/dist/esm/core/drawingml/text-warp.js +49 -1
- package/dist/esm/core/font/index.d.ts +1 -1
- package/dist/esm/core/font/index.js +8 -0
- package/dist/esm/core/font/indic-shaper.d.ts +47 -0
- package/dist/esm/core/font/indic-shaper.js +1146 -0
- package/dist/esm/core/font/indic-table.d.ts +12 -0
- package/dist/esm/core/font/indic-table.js +463 -0
- package/dist/esm/core/font/measure.d.ts +45 -4
- package/dist/esm/core/font/measure.js +253 -14
- package/dist/esm/core/font/opentype-lookups.d.ts +214 -0
- package/dist/esm/core/font/opentype-lookups.js +1112 -0
- package/dist/esm/core/fonts/families.d.ts +4 -0
- package/dist/esm/core/fonts/families.js +43 -12
- package/dist/esm/core/fonts/provider.js +1 -0
- package/dist/esm/core/fonts/remote-fonts.d.ts +4 -2
- package/dist/esm/core/fonts/remote-fonts.js +36 -0
- package/dist/esm/core/fonts/scripts.d.ts +19 -0
- package/dist/esm/core/fonts/scripts.js +96 -1
- package/dist/esm/core/numbering/apply.js +12 -8
- package/dist/esm/core/style-cascade/index.d.ts +1 -1
- package/dist/esm/core/style-cascade/index.js +2 -2
- package/dist/esm/core/style-cascade/resolver.d.ts +9 -0
- package/dist/esm/core/style-cascade/resolver.js +31 -3
- package/dist/esm/core/style-cascade/table.js +3 -8
- package/dist/esm/excel/xlsx-to-pdf.js +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/layout/line-paint.d.ts +5 -4
- package/dist/esm/layout/line-paint.js +6 -5
- package/dist/esm/layout/page-doc.d.ts +26 -0
- package/dist/esm/layout/styled-layout.d.ts +8 -1
- package/dist/esm/layout/styled-layout.js +459 -112
- package/dist/esm/layout/undrawn.d.ts +18 -0
- package/dist/esm/layout/undrawn.js +59 -0
- package/dist/esm/pdf/cid-font.d.ts +5 -0
- package/dist/esm/pdf/cid-font.js +8 -2
- package/dist/esm/pdf/styled-page-emitter.js +117 -28
- package/dist/esm/pdf/text-page-renderer.js +1 -0
- package/dist/esm/pdf-reader/content.d.ts +1 -1
- package/dist/esm/pdf-reader/content.js +48 -13
- package/dist/esm/pdf-reader/embedded-fonts.js +1 -0
- package/dist/esm/pdf-reader/encodings.d.ts +9 -0
- package/dist/esm/pdf-reader/encodings.js +66 -1
- package/dist/esm/pdf-reader/face-outlines.js +1 -0
- package/dist/esm/pdf-reader/text.d.ts +10 -0
- package/dist/esm/pdf-reader/text.js +25 -3
- package/dist/esm/pdf-reader/vector.js +2 -2
- package/dist/esm/pptx/embedded-fonts.js +1 -0
- package/dist/esm/pptx/slide-parser.js +22 -7
- package/dist/esm/pptx/sp-helpers.d.ts +3 -2
- package/dist/esm/pptx/sp-helpers.js +9 -4
- package/dist/esm/svg/svg-text.js +30 -7
- package/dist/esm/word/doc/doc-reader.js +148 -57
- package/dist/esm/word/doc/doc-text.d.ts +80 -11
- package/dist/esm/word/doc/doc-text.js +438 -75
- package/dist/esm/word/document-parser.js +48 -28
- package/dist/esm/word/docx-to-pdf.d.ts +5 -5
- package/dist/esm/word/docx-to-pdf.js +9 -7
- package/dist/esm/word/docx-writer.js +3 -5
- package/dist/esm/word/font-embed.js +1 -0
- package/dist/esm/word/font-table.js +1 -0
- package/dist/esm/word/numbering-parser.js +3 -0
- package/dist/esm/word/paragraph-properties.js +2 -2
- package/dist/esm/word/run-properties.js +2 -2
- package/dist/esm/word/xml-helpers.d.ts +18 -0
- package/dist/esm/word/xml-helpers.js +48 -1
- package/package.json +3 -3
|
@@ -734,22 +734,24 @@ function collectRuns(container, out, ctx, href, anchor, anchored, sdtRunProps) {
|
|
|
734
734
|
if (poIs(child, "w:r")) {
|
|
735
735
|
const parsed = parseRun(child, ctx, anchored);
|
|
736
736
|
const ranges = ctx.openCommentRanges && ctx.openCommentRanges.size > 0 ? [...ctx.openCommentRanges] : void 0;
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
...sdtRunProps
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
737
|
+
for (const own of parsed.runs) {
|
|
738
|
+
const run = href !== void 0 || anchor !== void 0 || ranges !== void 0 || sdtRunProps ? {
|
|
739
|
+
...own,
|
|
740
|
+
...sdtRunProps ? { properties: {
|
|
741
|
+
...sdtRunProps,
|
|
742
|
+
...own.properties
|
|
743
|
+
} } : {},
|
|
744
|
+
...href !== void 0 ? { href } : {},
|
|
745
|
+
...anchor !== void 0 ? { anchor } : {},
|
|
746
|
+
...ranges !== void 0 ? { commentRangeRefs: ranges } : {}
|
|
747
|
+
} : own;
|
|
748
|
+
out.push({
|
|
749
|
+
run,
|
|
750
|
+
...parsed.fldChar ? { fldChar: parsed.fldChar } : {},
|
|
751
|
+
...parsed.instrText !== void 0 ? { instrText: parsed.instrText } : {},
|
|
752
|
+
...parsed.checkBox !== void 0 ? { checkBox: parsed.checkBox } : {}
|
|
753
|
+
});
|
|
754
|
+
}
|
|
753
755
|
continue;
|
|
754
756
|
}
|
|
755
757
|
if (poIs(child, "m:oMath")) {
|
|
@@ -890,6 +892,9 @@ function symbolChar(font, char) {
|
|
|
890
892
|
function parseRun(r, ctx, anchored) {
|
|
891
893
|
const rPr = poChildren(r).find((c) => poIs(c, "w:rPr"));
|
|
892
894
|
const properties = parseRunProperties(rPr ? poElementToFlat(rPr) : void 0, ctx.themeFonts);
|
|
895
|
+
const children = expandMcChildren(poChildren(r));
|
|
896
|
+
const cuts = !children.some((c) => poIs(c, "w:fldChar") || poIs(c, "w:instrText"));
|
|
897
|
+
const runs = [];
|
|
893
898
|
let text = "";
|
|
894
899
|
let pageBreak = false;
|
|
895
900
|
let columnBreak = false;
|
|
@@ -901,7 +906,28 @@ function parseRun(r, ctx, anchored) {
|
|
|
901
906
|
let endnoteRef;
|
|
902
907
|
let commentRef;
|
|
903
908
|
let noteNumber = false;
|
|
904
|
-
|
|
909
|
+
const cut = (last) => {
|
|
910
|
+
if (text !== "" || inlineImage !== void 0 || pageBreak || columnBreak || footnoteRef !== void 0 || endnoteRef !== void 0 || commentRef !== void 0 || noteNumber || last && runs.length === 0) runs.push({
|
|
911
|
+
text,
|
|
912
|
+
properties,
|
|
913
|
+
...inlineImage ? { inlineImage } : {},
|
|
914
|
+
...pageBreak ? { pageBreak: true } : {},
|
|
915
|
+
...columnBreak ? { columnBreak: true } : {},
|
|
916
|
+
...footnoteRef !== void 0 ? { footnoteRef } : {},
|
|
917
|
+
...endnoteRef !== void 0 ? { endnoteRef } : {},
|
|
918
|
+
...commentRef !== void 0 ? { commentRef } : {},
|
|
919
|
+
...noteNumber ? { noteNumber: true } : {}
|
|
920
|
+
});
|
|
921
|
+
text = "";
|
|
922
|
+
pageBreak = false;
|
|
923
|
+
columnBreak = false;
|
|
924
|
+
inlineImage = void 0;
|
|
925
|
+
footnoteRef = void 0;
|
|
926
|
+
endnoteRef = void 0;
|
|
927
|
+
commentRef = void 0;
|
|
928
|
+
noteNumber = false;
|
|
929
|
+
};
|
|
930
|
+
for (const child of children) {
|
|
905
931
|
if (poIs(child, "w:rPr")) continue;
|
|
906
932
|
if (poIs(child, "w:fldChar")) {
|
|
907
933
|
const t = poAttr(child, "fldCharType");
|
|
@@ -937,11 +963,14 @@ function parseRun(r, ctx, anchored) {
|
|
|
937
963
|
else if (poIs(child, "w:ptab")) text += " ";
|
|
938
964
|
else if (poIs(child, "w:br")) {
|
|
939
965
|
const breakType = poAttr(child, "type");
|
|
966
|
+
const forced = cuts && (breakType === "page" || breakType === "column");
|
|
967
|
+
if (forced) cut(false);
|
|
940
968
|
if (breakType === "page") pageBreak = true;
|
|
941
969
|
else {
|
|
942
970
|
if (breakType === "column") columnBreak = true;
|
|
943
971
|
text += "\n";
|
|
944
972
|
}
|
|
973
|
+
if (forced) cut(false);
|
|
945
974
|
} else if (poIs(child, "w:sym")) text += symbolChar(poAttr(child, "font"), poAttr(child, "char"));
|
|
946
975
|
else if (poIs(child, "w:noBreakHyphen")) text += "‑";
|
|
947
976
|
else if (poIs(child, "w:softHyphen")) text += "";
|
|
@@ -981,18 +1010,9 @@ function parseRun(r, ctx, anchored) {
|
|
|
981
1010
|
} else if (content && anchored) anchored.push(...blocksForDrawing(content, {}, ctx) ?? []);
|
|
982
1011
|
}
|
|
983
1012
|
}
|
|
1013
|
+
cut(true);
|
|
984
1014
|
return {
|
|
985
|
-
|
|
986
|
-
text,
|
|
987
|
-
properties,
|
|
988
|
-
...inlineImage ? { inlineImage } : {},
|
|
989
|
-
...pageBreak ? { pageBreak: true } : {},
|
|
990
|
-
...columnBreak ? { columnBreak: true } : {},
|
|
991
|
-
...footnoteRef !== void 0 ? { footnoteRef } : {},
|
|
992
|
-
...endnoteRef !== void 0 ? { endnoteRef } : {},
|
|
993
|
-
...commentRef !== void 0 ? { commentRef } : {},
|
|
994
|
-
...noteNumber ? { noteNumber: true } : {}
|
|
995
|
-
},
|
|
1015
|
+
runs,
|
|
996
1016
|
...fldChar ? { fldChar } : {},
|
|
997
1017
|
...instrText !== void 0 ? { instrText } : {},
|
|
998
1018
|
...checkBox !== void 0 ? { checkBox } : {}
|
|
@@ -38,11 +38,11 @@ export interface ConvertDocxOptions extends Omit<StyledRenderOptions, 'registry'
|
|
|
38
38
|
*/
|
|
39
39
|
export declare function convertDocxToPdfSync(docx: Uint8Array, options: ConvertDocxOptions): Uint8Array;
|
|
40
40
|
/**
|
|
41
|
-
* The distinct curated font families the document references — the `w:ascii`
|
|
42
|
-
* `styles.xml` + `document.xml`, and the theme slots they point
|
|
43
|
-
* (§17.3.2.26) — always including the sans default `'arimo'` as a
|
|
44
|
-
* unstyled runs / math / charts. The async path fetches one
|
|
45
|
-
* family so each run renders in the right one.
|
|
41
|
+
* The distinct curated font families the document references — the `w:ascii`
|
|
42
|
+
* and `w:hAnsi` of `styles.xml` + `document.xml`, and the theme slots they point
|
|
43
|
+
* at instead (§17.3.2.26) — always including the sans default `'arimo'` as a
|
|
44
|
+
* fallback for unstyled runs / math / charts. The async path fetches one
|
|
45
|
+
* substitute set per family so each run renders in the right one.
|
|
46
46
|
*
|
|
47
47
|
* @param docx The `.docx` bytes.
|
|
48
48
|
* @returns The resolved {@link FamilyKey} set; just the default when the package
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { fetchFontSet, resolveFamilyKey } from "../core/fonts/remote-fonts.js";
|
|
2
2
|
import { FontRegistry } from "../core/font/font-registry.js";
|
|
3
|
+
import "../core/font/index.js";
|
|
3
4
|
import { signPdf } from "../pdf/signature.js";
|
|
4
5
|
import { renderStyledPdf, renderStyledPdfEncrypted } from "../pdf/styled-page-renderer.js";
|
|
5
6
|
import "../pdf/index.js";
|
|
@@ -15,6 +16,7 @@ var STYLES_PART = "word/styles.xml";
|
|
|
15
16
|
var THEME_PART = "word/theme/theme1.xml";
|
|
16
17
|
var MAIN_DOCUMENT_PART = "word/document.xml";
|
|
17
18
|
var ASCII_FONT = /<w:rFonts[^>]*?\bw:(ascii|asciiTheme)="([^"]+)"/y;
|
|
19
|
+
var HANSI_FONT = /<w:rFonts[^>]*?\bw:(hAnsi|hAnsiTheme)="([^"]+)"/y;
|
|
18
20
|
/**
|
|
19
21
|
* Synchronous one-shot conversion (requires `fonts`/`fontBytes`, no network).
|
|
20
22
|
*
|
|
@@ -54,11 +56,11 @@ function prepareDocxStyledRender(docx, options) {
|
|
|
54
56
|
};
|
|
55
57
|
}
|
|
56
58
|
/**
|
|
57
|
-
* The distinct curated font families the document references — the `w:ascii`
|
|
58
|
-
* `styles.xml` + `document.xml`, and the theme slots they point
|
|
59
|
-
* (§17.3.2.26) — always including the sans default `'arimo'` as a
|
|
60
|
-
* unstyled runs / math / charts. The async path fetches one
|
|
61
|
-
* family so each run renders in the right one.
|
|
59
|
+
* The distinct curated font families the document references — the `w:ascii`
|
|
60
|
+
* and `w:hAnsi` of `styles.xml` + `document.xml`, and the theme slots they point
|
|
61
|
+
* at instead (§17.3.2.26) — always including the sans default `'arimo'` as a
|
|
62
|
+
* fallback for unstyled runs / math / charts. The async path fetches one
|
|
63
|
+
* substitute set per family so each run renders in the right one.
|
|
62
64
|
*
|
|
63
65
|
* @param docx The `.docx` bytes.
|
|
64
66
|
* @returns The resolved {@link FamilyKey} set; just the default when the package
|
|
@@ -78,8 +80,8 @@ function detectDocxFamilyKeys(docx) {
|
|
|
78
80
|
const data = pkg.getPart(part);
|
|
79
81
|
if (!data) continue;
|
|
80
82
|
const xml = decoder.decode(data);
|
|
81
|
-
for (const m of tagMatches(xml, "<w:rFonts", false,
|
|
82
|
-
const name = m[1] === "ascii" ? m[2] : resolveWordThemeFont(m[2], themeFonts);
|
|
83
|
+
for (const slot of [ASCII_FONT, HANSI_FONT]) for (const m of tagMatches(xml, "<w:rFonts", false, slot)) {
|
|
84
|
+
const name = m[1] === "ascii" || m[1] === "hAnsi" ? m[2] : resolveWordThemeFont(m[2], themeFonts);
|
|
83
85
|
if (name !== void 0) keys.add(resolveFamilyKey(name));
|
|
84
86
|
}
|
|
85
87
|
}
|
|
@@ -1235,11 +1235,9 @@ function numberingXml(numbering) {
|
|
|
1235
1235
|
return out.join("");
|
|
1236
1236
|
}
|
|
1237
1237
|
function levelXml(level) {
|
|
1238
|
-
const inner = [
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
`<w:lvlText w:val="${escapeAttr(level.lvlText)}"/>`
|
|
1242
|
-
];
|
|
1238
|
+
const inner = [`<w:start w:val="${level.start}"/>`, `<w:numFmt w:val="${level.format}"/>`];
|
|
1239
|
+
if (level.suffix !== void 0) inner.push(`<w:suff w:val="${level.suffix}"/>`);
|
|
1240
|
+
inner.push(`<w:lvlText w:val="${escapeAttr(level.lvlText)}"/>`);
|
|
1243
1241
|
const pPr = rawParaPrXml(level.paragraphProperties);
|
|
1244
1242
|
if (pPr) inner.push(pPr);
|
|
1245
1243
|
const rPr = rawRunPrXml(level.runProperties);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildTrueType, editableEmbedding } from "../core/font/ttf-build.js";
|
|
2
|
+
import "../core/font/index.js";
|
|
2
3
|
import { FEATURES } from "../core/ir/features.js";
|
|
3
4
|
import { obfuscateEmbeddedFont } from "./font-table.js";
|
|
4
5
|
import { md5 } from "../core/crypto/primitives.js";
|
|
@@ -126,12 +126,15 @@ function parseLevel(node, picBullets, themeFonts) {
|
|
|
126
126
|
const picId = getValVal(lvlEl["w:lvlPicBulletId"]);
|
|
127
127
|
const picBullet = picId !== void 0 ? picBullets.get(picId) : void 0;
|
|
128
128
|
const isLegal = "w:isLgl" in lvlEl && getValVal(lvlEl["w:isLgl"]) !== "0";
|
|
129
|
+
const suff = getValVal(lvlEl["w:suff"]);
|
|
130
|
+
const suffix = suff === "space" || suff === "nothing" ? suff : void 0;
|
|
129
131
|
return {
|
|
130
132
|
ilvl,
|
|
131
133
|
start: Number.isFinite(start) ? start : 1,
|
|
132
134
|
format,
|
|
133
135
|
lvlText,
|
|
134
136
|
...isLegal ? { isLegal: true } : {},
|
|
137
|
+
...suffix ? { suffix } : {},
|
|
135
138
|
...picBullet ? { picBullet } : {},
|
|
136
139
|
paragraphProperties: parseParagraphProperties(lvlEl["w:pPr"], 0, themeFonts),
|
|
137
140
|
runProperties: parseRunProperties(lvlEl["w:rPr"], themeFonts)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { eighthPtToPt, pt, twipsToPt } from "../core/ir/units.js";
|
|
2
|
-
import { asArray, asElement, getAttr, getVal, parseIntAttr, parseToggle } from "./xml-helpers.js";
|
|
2
|
+
import { asArray, asElement, foldRepeatedProperties, getAttr, getVal, parseIntAttr, parseToggle } from "./xml-helpers.js";
|
|
3
3
|
import { parseRunProperties } from "./run-properties.js";
|
|
4
4
|
//#region src/word/paragraph-properties.ts
|
|
5
5
|
var ALIGNMENTS = /* @__PURE__ */ new Set([
|
|
@@ -105,7 +105,7 @@ function parseFramePr(node) {
|
|
|
105
105
|
* @returns The extracted properties; an empty object when `pPr` is absent.
|
|
106
106
|
*/
|
|
107
107
|
function parseParagraphProperties(pPr, autoSpacingPt = 0, themeFonts) {
|
|
108
|
-
const el =
|
|
108
|
+
const el = foldRepeatedProperties(pPr);
|
|
109
109
|
if (!el) return {};
|
|
110
110
|
const out = {};
|
|
111
111
|
if ("w:pStyle" in el) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { halfPtToPt, twipsToPt } from "../core/ir/units.js";
|
|
2
2
|
import { resolveThemeFont } from "../core/drawingml/theme-parser.js";
|
|
3
|
-
import { asElement, getAttr, getVal, parseHalfPointAttr, parseToggle } from "./xml-helpers.js";
|
|
3
|
+
import { asElement, foldRepeatedProperties, getAttr, getVal, parseHalfPointAttr, parseToggle } from "./xml-helpers.js";
|
|
4
4
|
import { resolveWordThemeFont } from "./theme-fonts.js";
|
|
5
5
|
import { shadingFillHex } from "./shading.js";
|
|
6
6
|
//#region src/word/run-properties.ts
|
|
@@ -69,7 +69,7 @@ var HIGHLIGHT_COLORS = /* @__PURE__ */ new Map([
|
|
|
69
69
|
* @returns The parsed properties; an empty object when `rPr` is absent.
|
|
70
70
|
*/
|
|
71
71
|
function parseRunProperties(rPr, themeFonts) {
|
|
72
|
-
const el =
|
|
72
|
+
const el = foldRepeatedProperties(rPr);
|
|
73
73
|
if (!el) return {};
|
|
74
74
|
const out = {};
|
|
75
75
|
if ("w:rStyle" in el) {
|
|
@@ -58,6 +58,24 @@ export declare function parseIntAttr(node: unknown, name: string): number | unde
|
|
|
58
58
|
* @returns The toggle state, or `undefined` when the property is absent.
|
|
59
59
|
*/
|
|
60
60
|
export declare function parseToggle(node: unknown): boolean | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Narrow a `w:rPr` or `w:pPr` to an element, with every property written more
|
|
63
|
+
* than once in it folded into the one Word applies.
|
|
64
|
+
*
|
|
65
|
+
* The schema allows each property once, but producers repeat them: LibreOffice
|
|
66
|
+
* writes `<w:b w:val="false"/>` twice, and the flat tree hands the pair over as
|
|
67
|
+
* an array, which every reader took for nothing and {@link parseToggle} for
|
|
68
|
+
* "on", so the run came out bold. Word applies the occurrences in order, each
|
|
69
|
+
* over the ones before it (Word for Mac, measured): what a later one states
|
|
70
|
+
* wins, what it leaves unsaid stands. Two `w:ind` keep the left indent of one
|
|
71
|
+
* and the right of the other, two `w:pBdr` draw the rules of both, and two
|
|
72
|
+
* `w:rFonts` name the faces of both.
|
|
73
|
+
*
|
|
74
|
+
* @param node The parsed `w:rPr` / `w:pPr`.
|
|
75
|
+
* @returns The element with its repeats folded (the same object when it has
|
|
76
|
+
* none), or `undefined` when `node` is not a single element.
|
|
77
|
+
*/
|
|
78
|
+
export declare function foldRepeatedProperties(node: unknown): XmlElement | undefined;
|
|
61
79
|
/**
|
|
62
80
|
* Read the `#text` content of a node. A bare string/number is returned directly;
|
|
63
81
|
* an element returns its `#text` child (coerced to string), else the empty string.
|
|
@@ -90,5 +90,52 @@ function parseToggle(node) {
|
|
|
90
90
|
if (v === void 0) return true;
|
|
91
91
|
return !(v === "false" || v === "0" || v === "off");
|
|
92
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Narrow a `w:rPr` or `w:pPr` to an element, with every property written more
|
|
95
|
+
* than once in it folded into the one Word applies.
|
|
96
|
+
*
|
|
97
|
+
* The schema allows each property once, but producers repeat them: LibreOffice
|
|
98
|
+
* writes `<w:b w:val="false"/>` twice, and the flat tree hands the pair over as
|
|
99
|
+
* an array, which every reader took for nothing and {@link parseToggle} for
|
|
100
|
+
* "on", so the run came out bold. Word applies the occurrences in order, each
|
|
101
|
+
* over the ones before it (Word for Mac, measured): what a later one states
|
|
102
|
+
* wins, what it leaves unsaid stands. Two `w:ind` keep the left indent of one
|
|
103
|
+
* and the right of the other, two `w:pBdr` draw the rules of both, and two
|
|
104
|
+
* `w:rFonts` name the faces of both.
|
|
105
|
+
*
|
|
106
|
+
* @param node The parsed `w:rPr` / `w:pPr`.
|
|
107
|
+
* @returns The element with its repeats folded (the same object when it has
|
|
108
|
+
* none), or `undefined` when `node` is not a single element.
|
|
109
|
+
*/
|
|
110
|
+
function foldRepeatedProperties(node) {
|
|
111
|
+
const el = asElement(node);
|
|
112
|
+
if (!el) return void 0;
|
|
113
|
+
let out;
|
|
114
|
+
for (const [tag, v] of Object.entries(el)) {
|
|
115
|
+
if (!Array.isArray(v)) continue;
|
|
116
|
+
out ??= { ...el };
|
|
117
|
+
out[tag] = fold(v);
|
|
118
|
+
}
|
|
119
|
+
return out ?? el;
|
|
120
|
+
}
|
|
121
|
+
function fold(v) {
|
|
122
|
+
return Array.isArray(v) ? v.reduce(applyOver) : v;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* One occurrence of a property element applied over an earlier one: its
|
|
126
|
+
* attributes win, its child elements apply over theirs in turn, and its tab
|
|
127
|
+
* stops join the ones already set (Word keeps the stops of both `w:tabs`).
|
|
128
|
+
*/
|
|
129
|
+
function applyOver(earlier, later) {
|
|
130
|
+
const a = asElement(earlier) ?? {};
|
|
131
|
+
const b = asElement(later) ?? {};
|
|
132
|
+
if (valOnly(a) && valOnly(b)) return later;
|
|
133
|
+
const out = { ...a };
|
|
134
|
+
for (const [k, v] of Object.entries(b)) out[k] = !Object.hasOwn(out, k) || k.startsWith("@_") ? v : k === "w:tab" ? [...asArray(out[k]), ...asArray(v)] : applyOver(fold(out[k]), fold(v));
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
function valOnly(el) {
|
|
138
|
+
return Object.keys(el).every((k) => k === "@_w:val" || k === "@_val");
|
|
139
|
+
}
|
|
93
140
|
//#endregion
|
|
94
|
-
export { asArray, asElement, getAttr, getVal, parseHalfPointAttr, parseIntAttr, parseToggle };
|
|
141
|
+
export { asArray, asElement, foldRepeatedProperties, getAttr, getVal, parseHalfPointAttr, parseIntAttr, parseToggle };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reamkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.34.0",
|
|
4
4
|
"description": "Ream — convert DOCX, XLSX, PPTX, PDF and legacy DOC, XLS and PPT to PDF, SVG, HTML, Markdown, DOCX and XLSX, built from scratch on the ECMA-376 and ISO 32000 specifications. Parse once, convert anywhere.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alexandr Krassavin <info@reamkit.dev>",
|
|
@@ -102,8 +102,8 @@
|
|
|
102
102
|
"publint": "^0.3.21",
|
|
103
103
|
"tsx": "^4.19.0",
|
|
104
104
|
"typescript": "^5.7.0",
|
|
105
|
-
"vite": "8.3.
|
|
106
|
-
"vitest": "
|
|
105
|
+
"vite": "8.3.5",
|
|
106
|
+
"vitest": "5.0.5"
|
|
107
107
|
},
|
|
108
108
|
"overrides": {
|
|
109
109
|
"@microsoft/api-extractor": {
|