@yinyoudexing/xml2word 0.1.0 → 0.1.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/{createDocxZip-WVDRDYZT.js → createDocxZip-WGQSPGIF.js} +49 -6
- package/dist/createDocxZip-WGQSPGIF.js.map +1 -0
- package/dist/{htmlToWordBodyXml-RFBPSL2Q.js → htmlToWordBodyXml-AG3GTZEZ.js} +233 -22
- package/dist/htmlToWordBodyXml-AG3GTZEZ.js.map +1 -0
- package/dist/index.cjs +314 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +30 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/createDocxZip-WVDRDYZT.js.map +0 -1
- package/dist/htmlToWordBodyXml-RFBPSL2Q.js.map +0 -1
|
@@ -78,13 +78,25 @@ function validateXmlIfNeeded(xml, validateXml) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// src/lib/createDocxZip.ts
|
|
81
|
-
|
|
81
|
+
function buildContentTypesXml(assets) {
|
|
82
|
+
const defaults = /* @__PURE__ */ new Map();
|
|
83
|
+
defaults.set("rels", "application/vnd.openxmlformats-package.relationships+xml");
|
|
84
|
+
defaults.set("xml", "application/xml");
|
|
85
|
+
for (const asset of assets) {
|
|
86
|
+
if (!asset.contentType) continue;
|
|
87
|
+
const extMatch = asset.target.match(/\.([a-zA-Z0-9]+)$/);
|
|
88
|
+
if (!extMatch) continue;
|
|
89
|
+
const ext = extMatch[1].toLowerCase();
|
|
90
|
+
defaults.set(ext, asset.contentType);
|
|
91
|
+
}
|
|
92
|
+
const defaultLines = [...defaults.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([ext, ct]) => ` <Default Extension="${ext}" ContentType="${ct}"/>`).join("\n");
|
|
93
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
82
94
|
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
83
|
-
|
|
84
|
-
<Default Extension="xml" ContentType="application/xml"/>
|
|
95
|
+
${defaultLines}
|
|
85
96
|
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
|
|
86
97
|
</Types>
|
|
87
98
|
`;
|
|
99
|
+
}
|
|
88
100
|
var ROOT_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
89
101
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
90
102
|
<Relationship Id="rId1"
|
|
@@ -92,18 +104,49 @@ var ROOT_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
|
92
104
|
Target="word/document.xml"/>
|
|
93
105
|
</Relationships>
|
|
94
106
|
`;
|
|
107
|
+
function buildDocumentRelsXml(assets) {
|
|
108
|
+
const relLines = assets.map(
|
|
109
|
+
(a) => ` <Relationship Id="${a.relationshipId}" Type="${a.relationshipType}" Target="${a.target}"/>`
|
|
110
|
+
).join("\n");
|
|
111
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
112
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
113
|
+
${relLines}
|
|
114
|
+
</Relationships>
|
|
115
|
+
`;
|
|
116
|
+
}
|
|
95
117
|
async function createDocxZipUint8Array(xml, options = {}) {
|
|
96
118
|
const documentXml = normalizeDocumentXml(xml, options.inputKind ?? "auto");
|
|
97
119
|
validateXmlIfNeeded(documentXml, options.validateXml ?? true);
|
|
98
120
|
const zip = new JSZip();
|
|
99
|
-
zip.file("[Content_Types].xml",
|
|
121
|
+
zip.file("[Content_Types].xml", buildContentTypesXml([]));
|
|
100
122
|
const relsFolder = zip.folder("_rels");
|
|
101
123
|
relsFolder?.file(".rels", ROOT_RELS_XML);
|
|
102
124
|
const wordFolder = zip.folder("word");
|
|
103
125
|
wordFolder?.file("document.xml", documentXml);
|
|
104
126
|
return zip.generateAsync({ type: "uint8array" });
|
|
105
127
|
}
|
|
128
|
+
async function createDocxZipWithAssetsUint8Array(xml, options, assets) {
|
|
129
|
+
const documentXml = normalizeDocumentXml(xml, options.inputKind ?? "auto");
|
|
130
|
+
validateXmlIfNeeded(documentXml, options.validateXml ?? true);
|
|
131
|
+
const zip = new JSZip();
|
|
132
|
+
zip.file("[Content_Types].xml", buildContentTypesXml(assets));
|
|
133
|
+
const relsFolder = zip.folder("_rels");
|
|
134
|
+
relsFolder?.file(".rels", ROOT_RELS_XML);
|
|
135
|
+
const wordFolder = zip.folder("word");
|
|
136
|
+
wordFolder?.file("document.xml", documentXml);
|
|
137
|
+
if (assets.length) {
|
|
138
|
+
const wordRelsFolder = wordFolder?.folder("_rels");
|
|
139
|
+
wordRelsFolder?.file("document.xml.rels", buildDocumentRelsXml(assets));
|
|
140
|
+
for (const asset of assets) {
|
|
141
|
+
const targetPath = asset.target.replace(/^\.\//, "");
|
|
142
|
+
const normalized = targetPath.startsWith("word/") ? targetPath.slice("word/".length) : targetPath;
|
|
143
|
+
wordFolder?.file(normalized, asset.data);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return zip.generateAsync({ type: "uint8array" });
|
|
147
|
+
}
|
|
106
148
|
export {
|
|
107
|
-
createDocxZipUint8Array
|
|
149
|
+
createDocxZipUint8Array,
|
|
150
|
+
createDocxZipWithAssetsUint8Array
|
|
108
151
|
};
|
|
109
|
-
//# sourceMappingURL=createDocxZip-
|
|
152
|
+
//# sourceMappingURL=createDocxZip-WGQSPGIF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/createDocxZip.ts","../src/lib/normalizeDocumentXml.ts","../src/lib/validateXml.ts"],"sourcesContent":["import JSZip from \"jszip\";\nimport type { XmlToDocxOptions } from \"../index.js\";\nimport { normalizeDocumentXml } from \"./normalizeDocumentXml.js\";\nimport { validateXmlIfNeeded } from \"./validateXml.js\";\n\nexport type DocxAsset = {\n relationshipId: string;\n relationshipType: string;\n target: string;\n data: Uint8Array;\n contentType?: string;\n};\n\nfunction buildContentTypesXml(assets: DocxAsset[]): string {\n const defaults = new Map<string, string>();\n defaults.set(\"rels\", \"application/vnd.openxmlformats-package.relationships+xml\");\n defaults.set(\"xml\", \"application/xml\");\n\n for (const asset of assets) {\n if (!asset.contentType) continue;\n const extMatch = asset.target.match(/\\.([a-zA-Z0-9]+)$/);\n if (!extMatch) continue;\n const ext = extMatch[1].toLowerCase();\n defaults.set(ext, asset.contentType);\n }\n\n const defaultLines = [...defaults.entries()]\n .sort((a, b) => a[0].localeCompare(b[0]))\n .map(([ext, ct]) => ` <Default Extension=\"${ext}\" ContentType=\"${ct}\"/>`)\n .join(\"\\n\");\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\\n<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">\\n${defaultLines}\\n <Override PartName=\"/word/document.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml\"/>\\n</Types>\\n`;\n}\n\nconst ROOT_RELS_XML = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\n <Relationship Id=\"rId1\"\n Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\"\n Target=\"word/document.xml\"/>\n</Relationships>\n`;\n\nfunction buildDocumentRelsXml(assets: DocxAsset[]): string {\n const relLines = assets\n .map(\n (a) =>\n ` <Relationship Id=\"${a.relationshipId}\" Type=\"${a.relationshipType}\" Target=\"${a.target}\"/>`,\n )\n .join(\"\\n\");\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\\n<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">\\n${relLines}\\n</Relationships>\\n`;\n}\n\nexport async function createDocxZipUint8Array(\n xml: string,\n options: XmlToDocxOptions = {},\n): Promise<Uint8Array> {\n const documentXml = normalizeDocumentXml(xml, options.inputKind ?? \"auto\");\n validateXmlIfNeeded(documentXml, options.validateXml ?? true);\n\n const zip = new JSZip();\n zip.file(\"[Content_Types].xml\", buildContentTypesXml([]));\n\n const relsFolder = zip.folder(\"_rels\");\n relsFolder?.file(\".rels\", ROOT_RELS_XML);\n\n const wordFolder = zip.folder(\"word\");\n wordFolder?.file(\"document.xml\", documentXml);\n\n return zip.generateAsync({ type: \"uint8array\" });\n}\n\nexport async function createDocxZipWithAssetsUint8Array(\n xml: string,\n options: XmlToDocxOptions,\n assets: DocxAsset[],\n): Promise<Uint8Array> {\n const documentXml = normalizeDocumentXml(xml, options.inputKind ?? \"auto\");\n validateXmlIfNeeded(documentXml, options.validateXml ?? true);\n\n const zip = new JSZip();\n zip.file(\"[Content_Types].xml\", buildContentTypesXml(assets));\n\n const relsFolder = zip.folder(\"_rels\");\n relsFolder?.file(\".rels\", ROOT_RELS_XML);\n\n const wordFolder = zip.folder(\"word\");\n wordFolder?.file(\"document.xml\", documentXml);\n\n if (assets.length) {\n const wordRelsFolder = wordFolder?.folder(\"_rels\");\n wordRelsFolder?.file(\"document.xml.rels\", buildDocumentRelsXml(assets));\n\n for (const asset of assets) {\n const targetPath = asset.target.replace(/^\\.\\//, \"\");\n const normalized = targetPath.startsWith(\"word/\") ? targetPath.slice(\"word/\".length) : targetPath;\n wordFolder?.file(normalized, asset.data);\n }\n }\n\n return zip.generateAsync({ type: \"uint8array\" });\n}\n","import type { XmlToDocxInputKind } from \"../index.js\";\n\nconst WORD_MAIN_NS = \"http://schemas.openxmlformats.org/wordprocessingml/2006/main\";\n\nfunction hasWordDocumentRoot(xml: string): boolean {\n return /<w:document[\\s>]/.test(xml);\n}\n\nfunction ensureXmlDeclaration(xml: string): string {\n if (/^\\s*<\\?xml\\b/.test(xml)) return xml;\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\\n${xml}`;\n}\n\nfunction wrapBodyXml(bodyXml: string): string {\n const xml = `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<w:document\n xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\"\n xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"\n>\n <w:body>\n${bodyXml}\n <w:sectPr>\n <w:pgSz w:w=\"12240\" w:h=\"15840\"/>\n <w:pgMar w:top=\"1440\" w:right=\"1440\" w:bottom=\"1440\" w:left=\"1440\" w:header=\"708\" w:footer=\"708\" w:gutter=\"0\"/>\n <w:cols w:space=\"708\"/>\n <w:docGrid w:linePitch=\"360\"/>\n </w:sectPr>\n </w:body>\n</w:document>\n`;\n return xml;\n}\n\nfunction ensureWordNamespace(xml: string): string {\n const hasWNamespace =\n /xmlns:w\\s*=\\s*[\"']http:\\/\\/schemas\\.openxmlformats\\.org\\/wordprocessingml\\/2006\\/main[\"']/.test(\n xml,\n );\n if (hasWNamespace) return xml;\n\n return xml.replace(\n /<w:document\\b/,\n `<w:document xmlns:w=\"${WORD_MAIN_NS}\"`,\n );\n}\n\nexport function normalizeDocumentXml(xml: string, inputKind: XmlToDocxInputKind): string {\n const trimmed = xml.trim();\n if (!trimmed) {\n throw new Error(\"XML is empty.\");\n }\n\n if (inputKind === \"document\") {\n const withDecl = ensureXmlDeclaration(trimmed);\n const withNs = ensureWordNamespace(withDecl);\n if (!hasWordDocumentRoot(withNs)) {\n throw new Error('inputKind=\"document\" requires a <w:document> root.');\n }\n return withNs;\n }\n\n if (inputKind === \"body\") {\n return wrapBodyXml(trimmed);\n }\n\n if (hasWordDocumentRoot(trimmed)) {\n const withDecl = ensureXmlDeclaration(trimmed);\n return ensureWordNamespace(withDecl);\n }\n\n return wrapBodyXml(trimmed);\n}\n\n","import { XMLValidator } from \"fast-xml-parser\";\n\nexport function validateXmlIfNeeded(xml: string, validateXml: boolean): void {\n if (!validateXml) return;\n\n const result = XMLValidator.validate(xml);\n if (result === true) return;\n\n const err = (result as { err?: { msg?: string; line?: number; col?: number } }).err;\n const msg = err?.msg ?? \"Invalid XML.\";\n const line = err?.line;\n const col = err?.col;\n\n const location =\n typeof line === \"number\" && typeof col === \"number\" ? ` (line ${line}, col ${col})` : \"\";\n throw new Error(`${msg}${location}`);\n}\n\n"],"mappings":"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","names":[]}
|
|
@@ -106,7 +106,107 @@ function getTextContent(node) {
|
|
|
106
106
|
for (const c of children) out += getTextContent(c);
|
|
107
107
|
return out;
|
|
108
108
|
}
|
|
109
|
-
function
|
|
109
|
+
function decodeBase64ToUint8Array(base64) {
|
|
110
|
+
const BufferCtor = globalThis.Buffer;
|
|
111
|
+
if (BufferCtor) {
|
|
112
|
+
return new Uint8Array(BufferCtor.from(base64, "base64"));
|
|
113
|
+
}
|
|
114
|
+
const atobFn = globalThis.atob;
|
|
115
|
+
if (!atobFn) {
|
|
116
|
+
throw new Error("Base64 decode is not available in this environment.");
|
|
117
|
+
}
|
|
118
|
+
const bin = atobFn(base64);
|
|
119
|
+
const bytes = new Uint8Array(bin.length);
|
|
120
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
121
|
+
return bytes;
|
|
122
|
+
}
|
|
123
|
+
function parseImageDataUrl(src) {
|
|
124
|
+
const m = src.match(/^data:(image\/png|image\/jpeg);base64,([\s\S]+)$/i);
|
|
125
|
+
if (!m) return void 0;
|
|
126
|
+
const contentType = m[1].toLowerCase();
|
|
127
|
+
const base64 = m[2].replace(/\s+/g, "");
|
|
128
|
+
const data = decodeBase64ToUint8Array(base64);
|
|
129
|
+
const extension = contentType === "image/png" ? "png" : "jpeg";
|
|
130
|
+
return { contentType, data, extension };
|
|
131
|
+
}
|
|
132
|
+
function parseCssLengthToPx(value) {
|
|
133
|
+
if (!value) return void 0;
|
|
134
|
+
const v = value.trim().toLowerCase();
|
|
135
|
+
const px = v.match(/^(\d+(?:\.\d+)?)px$/);
|
|
136
|
+
if (px) return Math.max(1, Math.round(Number(px[1])));
|
|
137
|
+
return void 0;
|
|
138
|
+
}
|
|
139
|
+
function readUInt32BE(bytes, offset) {
|
|
140
|
+
if (offset < 0 || offset + 4 > bytes.length) return void 0;
|
|
141
|
+
return ((bytes[offset] ?? 0) << 24 | (bytes[offset + 1] ?? 0) << 16 | (bytes[offset + 2] ?? 0) << 8 | (bytes[offset + 3] ?? 0)) >>> 0;
|
|
142
|
+
}
|
|
143
|
+
function parsePngDimensions(data) {
|
|
144
|
+
if (data.length < 24) return void 0;
|
|
145
|
+
const signature = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
146
|
+
for (let i = 0; i < signature.length; i++) {
|
|
147
|
+
if ((data[i] ?? 0) !== signature[i]) return void 0;
|
|
148
|
+
}
|
|
149
|
+
const widthPx = readUInt32BE(data, 16);
|
|
150
|
+
const heightPx = readUInt32BE(data, 20);
|
|
151
|
+
if (!widthPx || !heightPx) return void 0;
|
|
152
|
+
return { widthPx, heightPx };
|
|
153
|
+
}
|
|
154
|
+
function parseJpegDimensions(data) {
|
|
155
|
+
if (data.length < 4) return void 0;
|
|
156
|
+
if (data[0] !== 255 || data[1] !== 216) return void 0;
|
|
157
|
+
let offset = 2;
|
|
158
|
+
while (offset + 4 <= data.length) {
|
|
159
|
+
if (data[offset] !== 255) {
|
|
160
|
+
offset++;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
while (offset < data.length && data[offset] === 255) offset++;
|
|
164
|
+
if (offset >= data.length) return void 0;
|
|
165
|
+
const marker = data[offset];
|
|
166
|
+
offset++;
|
|
167
|
+
const isStandalone = marker === 217 || marker === 218;
|
|
168
|
+
if (isStandalone) break;
|
|
169
|
+
if (offset + 2 > data.length) return void 0;
|
|
170
|
+
const length = data[offset] << 8 | data[offset + 1];
|
|
171
|
+
if (length < 2 || offset + length > data.length) return void 0;
|
|
172
|
+
const isSof = marker === 192 || marker === 193 || marker === 194 || marker === 195 || marker === 197 || marker === 198 || marker === 199 || marker === 201 || marker === 202 || marker === 203 || marker === 205 || marker === 206 || marker === 207;
|
|
173
|
+
if (isSof) {
|
|
174
|
+
if (offset + 7 > data.length) return void 0;
|
|
175
|
+
const heightPx = data[offset + 3] << 8 | data[offset + 4];
|
|
176
|
+
const widthPx = data[offset + 5] << 8 | data[offset + 6];
|
|
177
|
+
if (!widthPx || !heightPx) return void 0;
|
|
178
|
+
return { widthPx, heightPx };
|
|
179
|
+
}
|
|
180
|
+
offset += length;
|
|
181
|
+
}
|
|
182
|
+
return void 0;
|
|
183
|
+
}
|
|
184
|
+
function parseIntrinsicImageSizePx(contentType, data) {
|
|
185
|
+
if (contentType === "image/png") return parsePngDimensions(data);
|
|
186
|
+
if (contentType === "image/jpeg") return parseJpegDimensions(data);
|
|
187
|
+
return void 0;
|
|
188
|
+
}
|
|
189
|
+
function applyMaxBoxPx(size, maxBox) {
|
|
190
|
+
const w = Math.max(1, Math.round(size.widthPx));
|
|
191
|
+
const h = Math.max(1, Math.round(size.heightPx));
|
|
192
|
+
const scale = Math.min(1, maxBox.maxWidthPx / w, maxBox.maxHeightPx / h);
|
|
193
|
+
return { widthPx: Math.max(1, Math.round(w * scale)), heightPx: Math.max(1, Math.round(h * scale)) };
|
|
194
|
+
}
|
|
195
|
+
function computeImageSizePx(node, intrinsic) {
|
|
196
|
+
const wAttr = node.attribs?.width ? Number(node.attribs.width) : void 0;
|
|
197
|
+
const hAttr = node.attribs?.height ? Number(node.attribs.height) : void 0;
|
|
198
|
+
const css = parseStyleAttribute(node.attribs?.style);
|
|
199
|
+
const wCss = parseCssLengthToPx(css.width);
|
|
200
|
+
const hCss = parseCssLengthToPx(css.height);
|
|
201
|
+
const widthAttrPx = Number.isFinite(wAttr) && wAttr ? Math.max(1, Math.round(wAttr)) : void 0;
|
|
202
|
+
const heightAttrPx = Number.isFinite(hAttr) && hAttr ? Math.max(1, Math.round(hAttr)) : void 0;
|
|
203
|
+
const ratio = intrinsic && intrinsic.widthPx > 0 && intrinsic.heightPx > 0 ? intrinsic.heightPx / intrinsic.widthPx : widthAttrPx && heightAttrPx ? heightAttrPx / widthAttrPx : 0.5;
|
|
204
|
+
const widthPx = typeof wCss === "number" ? wCss : typeof widthAttrPx === "number" ? widthAttrPx : intrinsic?.widthPx ?? 300;
|
|
205
|
+
const heightPx = typeof hCss === "number" ? hCss : typeof heightAttrPx === "number" ? heightAttrPx : intrinsic?.heightPx ?? 150;
|
|
206
|
+
const finalSize = typeof wCss === "number" && typeof hCss !== "number" ? { widthPx, heightPx: Math.max(1, Math.round(widthPx * ratio)) } : typeof hCss === "number" && typeof wCss !== "number" ? { widthPx: Math.max(1, Math.round(heightPx / ratio)), heightPx } : typeof widthAttrPx === "number" && typeof heightAttrPx !== "number" && intrinsic ? { widthPx, heightPx: Math.max(1, Math.round(widthPx * ratio)) } : typeof heightAttrPx === "number" && typeof widthAttrPx !== "number" && intrinsic ? { widthPx: Math.max(1, Math.round(heightPx / ratio)), heightPx } : { widthPx, heightPx };
|
|
207
|
+
return applyMaxBoxPx(finalSize, { maxWidthPx: 624, maxHeightPx: 864 });
|
|
208
|
+
}
|
|
209
|
+
function collectInlineRuns(node, inherited, out, result) {
|
|
110
210
|
if (node.type === "text") {
|
|
111
211
|
const text = node.data ?? "";
|
|
112
212
|
if (text) out.push({ kind: "text", text, style: inherited });
|
|
@@ -118,13 +218,57 @@ function collectInlineRuns(node, inherited, out) {
|
|
|
118
218
|
out.push({ kind: "br" });
|
|
119
219
|
return;
|
|
120
220
|
}
|
|
221
|
+
if (tag === "img") {
|
|
222
|
+
const src = node.attribs?.src;
|
|
223
|
+
if (!src) return;
|
|
224
|
+
const parsed = parseImageDataUrl(src);
|
|
225
|
+
if (!parsed) return;
|
|
226
|
+
const intrinsic = parseIntrinsicImageSizePx(parsed.contentType, parsed.data);
|
|
227
|
+
const { widthPx, heightPx } = computeImageSizePx(node, intrinsic);
|
|
228
|
+
const id = result.images.length + 1;
|
|
229
|
+
const relationshipId = `rId${id}`;
|
|
230
|
+
const target = `media/image${id}.${parsed.extension}`;
|
|
231
|
+
result.images.push({
|
|
232
|
+
relationshipId,
|
|
233
|
+
target,
|
|
234
|
+
data: parsed.data,
|
|
235
|
+
contentType: parsed.contentType,
|
|
236
|
+
widthPx,
|
|
237
|
+
heightPx
|
|
238
|
+
});
|
|
239
|
+
out.push({ kind: "image", image: { relationshipId, widthPx, heightPx } });
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (tag === "canvas") {
|
|
243
|
+
const dataUrl = node.attribs?.["data-image"] ?? node.attribs?.["data-src"];
|
|
244
|
+
if (!dataUrl) return;
|
|
245
|
+
const parsed = parseImageDataUrl(dataUrl);
|
|
246
|
+
if (!parsed) return;
|
|
247
|
+
const bufferW = node.attribs?.width ? Number(node.attribs.width) : void 0;
|
|
248
|
+
const bufferH = node.attribs?.height ? Number(node.attribs.height) : void 0;
|
|
249
|
+
const intrinsic = Number.isFinite(bufferW) && bufferW && Number.isFinite(bufferH) && bufferH ? { widthPx: Math.max(1, Math.round(bufferW)), heightPx: Math.max(1, Math.round(bufferH)) } : parseIntrinsicImageSizePx(parsed.contentType, parsed.data);
|
|
250
|
+
const { widthPx, heightPx } = computeImageSizePx(node, intrinsic);
|
|
251
|
+
const id = result.images.length + 1;
|
|
252
|
+
const relationshipId = `rId${id}`;
|
|
253
|
+
const target = `media/image${id}.${parsed.extension}`;
|
|
254
|
+
result.images.push({
|
|
255
|
+
relationshipId,
|
|
256
|
+
target,
|
|
257
|
+
data: parsed.data,
|
|
258
|
+
contentType: parsed.contentType,
|
|
259
|
+
widthPx,
|
|
260
|
+
heightPx
|
|
261
|
+
});
|
|
262
|
+
out.push({ kind: "image", image: { relationshipId, widthPx, heightPx } });
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
121
265
|
const next = mergeTextStyle(inherited, styleFromElement(node));
|
|
122
266
|
const children2 = node.children ?? [];
|
|
123
|
-
for (const c of children2) collectInlineRuns(c, next, out);
|
|
267
|
+
for (const c of children2) collectInlineRuns(c, next, out, result);
|
|
124
268
|
return;
|
|
125
269
|
}
|
|
126
270
|
const children = node.children ?? [];
|
|
127
|
-
for (const c of children) collectInlineRuns(c, inherited, out);
|
|
271
|
+
for (const c of children) collectInlineRuns(c, inherited, out, result);
|
|
128
272
|
}
|
|
129
273
|
function buildRunXml(style, text) {
|
|
130
274
|
const rPrParts = [];
|
|
@@ -145,6 +289,16 @@ function buildRunXml(style, text) {
|
|
|
145
289
|
const preserve = shouldPreserveSpace(text) ? ' xml:space="preserve"' : "";
|
|
146
290
|
return `<w:r>${rPrXml}<w:t${preserve}>${escaped}</w:t></w:r>`;
|
|
147
291
|
}
|
|
292
|
+
function pxToEmu(px) {
|
|
293
|
+
return Math.max(1, Math.round(px * 9525));
|
|
294
|
+
}
|
|
295
|
+
function buildImageRunXml(image) {
|
|
296
|
+
const cx = pxToEmu(image.widthPx);
|
|
297
|
+
const cy = pxToEmu(image.heightPx);
|
|
298
|
+
const docPrId = image.relationshipId.replace(/^rId/, "");
|
|
299
|
+
const name = `Picture ${docPrId}`;
|
|
300
|
+
return `<w:r><w:drawing xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${docPrId}" name="${escapeXmlText(name)}"/><a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic><pic:nvPicPr><pic:cNvPr id="0" name="${escapeXmlText(name)}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${image.relationshipId}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing></w:r>`;
|
|
301
|
+
}
|
|
148
302
|
function hasClass(node, className) {
|
|
149
303
|
const cls = node.attribs?.class;
|
|
150
304
|
if (!cls) return false;
|
|
@@ -153,7 +307,11 @@ function hasClass(node, className) {
|
|
|
153
307
|
function isSkippableSubtree(node) {
|
|
154
308
|
if (node.type !== "tag") return false;
|
|
155
309
|
const tag = node.name?.toLowerCase();
|
|
156
|
-
if (tag === "button"
|
|
310
|
+
if (tag === "button") return true;
|
|
311
|
+
if (tag === "canvas") {
|
|
312
|
+
const dataUrl = node.attribs?.["data-image"] ?? node.attribs?.["data-src"];
|
|
313
|
+
if (!dataUrl) return true;
|
|
314
|
+
}
|
|
157
315
|
if (tag === "img" && hasClass(node, "ProseMirror-separator")) return true;
|
|
158
316
|
if (node.attribs?.id === "pages") return true;
|
|
159
317
|
if (hasClass(node, "ProseMirror-widget")) return true;
|
|
@@ -245,17 +403,25 @@ function buildParagraphPrXml(node, baseFontHalfPoints, extraInd) {
|
|
|
245
403
|
if (!parts.length) return "";
|
|
246
404
|
return `<w:pPr>${parts.join("")}</w:pPr>`;
|
|
247
405
|
}
|
|
248
|
-
function buildParagraphXmlFromContainer(node, baseStyle, extraInd) {
|
|
406
|
+
function buildParagraphXmlFromContainer(node, baseStyle, extraInd, result) {
|
|
249
407
|
const baseFontHalfPoints = baseStyle.fontSizeHalfPoints ?? inferFirstFontSizeHalfPoints(node) ?? 28;
|
|
250
408
|
const pPrXml = buildParagraphPrXml(node, baseFontHalfPoints, extraInd);
|
|
251
409
|
const runs = [];
|
|
252
|
-
|
|
410
|
+
const res = result ?? {
|
|
411
|
+
bodyXml: "",
|
|
412
|
+
images: []
|
|
413
|
+
};
|
|
414
|
+
for (const c of node.children ?? []) collectInlineRuns(c, baseStyle, runs, res);
|
|
253
415
|
const rXml = [];
|
|
254
416
|
for (const token of runs) {
|
|
255
417
|
if (token.kind === "br") {
|
|
256
418
|
rXml.push("<w:r><w:br/></w:r>");
|
|
257
419
|
continue;
|
|
258
420
|
}
|
|
421
|
+
if (token.kind === "image") {
|
|
422
|
+
rXml.push(buildImageRunXml(token.image));
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
259
425
|
const text = token.text;
|
|
260
426
|
if (!text) continue;
|
|
261
427
|
if (!text.trim()) continue;
|
|
@@ -283,7 +449,7 @@ function buildHeadingBaseStyle(level) {
|
|
|
283
449
|
const size = level === 1 ? 44 : level === 2 ? 32 : level === 3 ? 28 : level === 4 ? 24 : 22;
|
|
284
450
|
return { bold: true, fontSizeHalfPoints: size };
|
|
285
451
|
}
|
|
286
|
-
function buildListBlocks(listNode, ordered) {
|
|
452
|
+
function buildListBlocks(listNode, ordered, result) {
|
|
287
453
|
const items = [];
|
|
288
454
|
const stack = [...listNode.children ?? []];
|
|
289
455
|
while (stack.length) {
|
|
@@ -297,13 +463,17 @@ function buildListBlocks(listNode, ordered) {
|
|
|
297
463
|
const baseStyle = {};
|
|
298
464
|
const runs = [];
|
|
299
465
|
runs.push({ kind: "text", text: prefix, style: baseStyle });
|
|
300
|
-
for (const c of li.children ?? []) collectInlineRuns(c, baseStyle, runs);
|
|
466
|
+
for (const c of li.children ?? []) collectInlineRuns(c, baseStyle, runs, result);
|
|
301
467
|
const rXml = [];
|
|
302
468
|
for (const token of runs) {
|
|
303
469
|
if (token.kind === "br") {
|
|
304
470
|
rXml.push("<w:r><w:br/></w:r>");
|
|
305
471
|
continue;
|
|
306
472
|
}
|
|
473
|
+
if (token.kind === "image") {
|
|
474
|
+
rXml.push(buildImageRunXml(token.image));
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
307
477
|
const text = token.text;
|
|
308
478
|
if (!text) continue;
|
|
309
479
|
if (!text.trim()) continue;
|
|
@@ -318,7 +488,7 @@ function buildListBlocks(listNode, ordered) {
|
|
|
318
488
|
}
|
|
319
489
|
return out;
|
|
320
490
|
}
|
|
321
|
-
function buildTableXml(tableNode) {
|
|
491
|
+
function buildTableXml(tableNode, result) {
|
|
322
492
|
const rows = [];
|
|
323
493
|
const stack = [...tableNode.children ?? []];
|
|
324
494
|
while (stack.length) {
|
|
@@ -335,7 +505,7 @@ function buildTableXml(tableNode) {
|
|
|
335
505
|
for (const cell of cells) {
|
|
336
506
|
const isHeader = cell.name === "th";
|
|
337
507
|
const baseStyle = isHeader ? { bold: true } : {};
|
|
338
|
-
const pXml = buildParagraphXmlFromContainer(cell, baseStyle);
|
|
508
|
+
const pXml = buildParagraphXmlFromContainer(cell, baseStyle, void 0, result);
|
|
339
509
|
const paragraphs = pXml ? pXml : "<w:p/>";
|
|
340
510
|
cellXml.push(
|
|
341
511
|
`<w:tc><w:tcPr><w:tcW w:w="0" w:type="auto"/></w:tcPr>${paragraphs}</w:tc>`
|
|
@@ -347,7 +517,16 @@ function buildTableXml(tableNode) {
|
|
|
347
517
|
const tblGrid = `<w:tblGrid/>`;
|
|
348
518
|
return `<w:tbl>${tblPr}${tblGrid}${rowXml.join("")}</w:tbl>`;
|
|
349
519
|
}
|
|
350
|
-
function
|
|
520
|
+
function buildParagraphXmlFromSingleInlineNode(node, baseStyle, result) {
|
|
521
|
+
const wrapper = {
|
|
522
|
+
type: "tag",
|
|
523
|
+
name: "p",
|
|
524
|
+
attribs: { style: "text-align: center;" },
|
|
525
|
+
children: [node]
|
|
526
|
+
};
|
|
527
|
+
return buildParagraphXmlFromContainer(wrapper, baseStyle, void 0, result);
|
|
528
|
+
}
|
|
529
|
+
function collectBodyBlocks(node, out, result) {
|
|
351
530
|
if (isSkippableSubtree(node)) return;
|
|
352
531
|
if (node.type === "tag") {
|
|
353
532
|
const tag = node.name?.toLowerCase();
|
|
@@ -356,27 +535,32 @@ function collectBodyBlocks(node, out) {
|
|
|
356
535
|
return;
|
|
357
536
|
}
|
|
358
537
|
if (tag === "p") {
|
|
359
|
-
const pXml = buildParagraphXmlFromContainer(node, {});
|
|
538
|
+
const pXml = buildParagraphXmlFromContainer(node, {}, void 0, result);
|
|
539
|
+
if (pXml) out.push(pXml);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
if (tag === "img" || tag === "canvas") {
|
|
543
|
+
const pXml = buildParagraphXmlFromSingleInlineNode(node, {}, result);
|
|
360
544
|
if (pXml) out.push(pXml);
|
|
361
545
|
return;
|
|
362
546
|
}
|
|
363
547
|
if (tag && /^h[1-6]$/.test(tag)) {
|
|
364
548
|
const level = Number(tag.slice(1));
|
|
365
|
-
const hXml = buildParagraphXmlFromContainer(node, buildHeadingBaseStyle(level));
|
|
549
|
+
const hXml = buildParagraphXmlFromContainer(node, buildHeadingBaseStyle(level), void 0, result);
|
|
366
550
|
if (hXml) out.push(hXml);
|
|
367
551
|
return;
|
|
368
552
|
}
|
|
369
553
|
if (tag === "table") {
|
|
370
|
-
const tblXml = buildTableXml(node);
|
|
554
|
+
const tblXml = buildTableXml(node, result);
|
|
371
555
|
if (tblXml) out.push(tblXml);
|
|
372
556
|
return;
|
|
373
557
|
}
|
|
374
558
|
if (tag === "ul" || tag === "ol") {
|
|
375
|
-
out.push(...buildListBlocks(node, tag === "ol"));
|
|
559
|
+
out.push(...buildListBlocks(node, tag === "ol", result));
|
|
376
560
|
return;
|
|
377
561
|
}
|
|
378
562
|
}
|
|
379
|
-
for (const c of node.children ?? []) collectBodyBlocks(c, out);
|
|
563
|
+
for (const c of node.children ?? []) collectBodyBlocks(c, out, result);
|
|
380
564
|
}
|
|
381
565
|
function textToWordBodyXml(text) {
|
|
382
566
|
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
@@ -394,23 +578,50 @@ function textToWordBodyXml(text) {
|
|
|
394
578
|
}
|
|
395
579
|
return out.join("");
|
|
396
580
|
}
|
|
397
|
-
function
|
|
581
|
+
function htmlToWordBody(html) {
|
|
398
582
|
const normalized = html.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
399
583
|
const doc = parseDocument(normalized, {
|
|
400
584
|
lowerCaseAttributeNames: true,
|
|
401
585
|
lowerCaseTags: true,
|
|
402
586
|
recognizeSelfClosing: true
|
|
403
587
|
});
|
|
588
|
+
const result = { bodyXml: "", images: [] };
|
|
404
589
|
const out = [];
|
|
405
|
-
collectBodyBlocks(doc, out);
|
|
406
|
-
|
|
407
|
-
|
|
590
|
+
collectBodyBlocks(doc, out, result);
|
|
591
|
+
result.bodyXml = out.join("");
|
|
592
|
+
return result;
|
|
593
|
+
}
|
|
594
|
+
function htmlToWordBodyXml(html) {
|
|
595
|
+
const { bodyXml } = htmlToWordBody(html);
|
|
596
|
+
if (!bodyXml) {
|
|
597
|
+
const text = getTextContent(
|
|
598
|
+
parseDocument(html, {
|
|
599
|
+
lowerCaseAttributeNames: true,
|
|
600
|
+
lowerCaseTags: true,
|
|
601
|
+
recognizeSelfClosing: true
|
|
602
|
+
})
|
|
603
|
+
);
|
|
408
604
|
return textToWordBodyXml(text);
|
|
409
605
|
}
|
|
410
|
-
return
|
|
606
|
+
return bodyXml;
|
|
607
|
+
}
|
|
608
|
+
function htmlToWordBodyWithAssets(html) {
|
|
609
|
+
const result = htmlToWordBody(html);
|
|
610
|
+
if (!result.bodyXml) {
|
|
611
|
+
const text = getTextContent(
|
|
612
|
+
parseDocument(html, {
|
|
613
|
+
lowerCaseAttributeNames: true,
|
|
614
|
+
lowerCaseTags: true,
|
|
615
|
+
recognizeSelfClosing: true
|
|
616
|
+
})
|
|
617
|
+
);
|
|
618
|
+
return { bodyXml: textToWordBodyXml(text), images: [] };
|
|
619
|
+
}
|
|
620
|
+
return result;
|
|
411
621
|
}
|
|
412
622
|
export {
|
|
623
|
+
htmlToWordBodyWithAssets,
|
|
413
624
|
htmlToWordBodyXml,
|
|
414
625
|
textToWordBodyXml
|
|
415
626
|
};
|
|
416
|
-
//# sourceMappingURL=htmlToWordBodyXml-
|
|
627
|
+
//# sourceMappingURL=htmlToWordBodyXml-AG3GTZEZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/htmlToWordBodyXml.ts"],"sourcesContent":["import { parseDocument } from \"htmlparser2\";\n\ntype HtmlNode = {\n type?: string;\n name?: string;\n data?: string;\n attribs?: Record<string, string | undefined>;\n children?: HtmlNode[];\n};\n\ntype TextStyle = {\n bold?: boolean;\n italic?: boolean;\n underline?: boolean;\n colorHex?: string;\n fontFamily?: string;\n fontSizeHalfPoints?: number;\n};\n\nfunction escapeXmlText(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction shouldPreserveSpace(text: string): boolean {\n if (!text) return false;\n return /^\\s/.test(text) || /\\s$/.test(text) || /\\s{2,}/.test(text);\n}\n\nfunction parseStyleAttribute(style: string | undefined): Record<string, string> {\n if (!style) return {};\n const normalized = style.replace(/\\r/g, \"\\n\");\n const parts = normalized.split(\";\");\n const entries: [string, string][] = [];\n for (const part of parts) {\n const idx = part.indexOf(\":\");\n if (idx <= 0) continue;\n const key = part.slice(0, idx).trim().toLowerCase();\n const val = part.slice(idx + 1).trim();\n if (!key || !val) continue;\n entries.push([key, val]);\n }\n return Object.fromEntries(entries);\n}\n\nfunction parseRgbToHex(value: string): string | undefined {\n const m = value\n .trim()\n .toLowerCase()\n .match(/^rgb\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*\\)$/);\n if (!m) return undefined;\n const nums = [Number(m[1]), Number(m[2]), Number(m[3])];\n if (nums.some((n) => Number.isNaN(n) || n < 0 || n > 255)) return undefined;\n return nums.map((n) => n.toString(16).padStart(2, \"0\")).join(\"\").toUpperCase();\n}\n\nfunction parseCssColorToHex(value: string | undefined): string | undefined {\n if (!value) return undefined;\n const v = value.trim();\n const hex = v.match(/^#([0-9a-fA-F]{6})$/)?.[1];\n if (hex) return hex.toUpperCase();\n return parseRgbToHex(v);\n}\n\nfunction parseFontSizeToHalfPoints(value: string | undefined): number | undefined {\n if (!value) return undefined;\n const v = value.trim().toLowerCase();\n const pt = v.match(/^(\\d+(?:\\.\\d+)?)pt$/);\n if (pt) return Math.max(1, Math.round(Number(pt[1]) * 2));\n const px = v.match(/^(\\d+(?:\\.\\d+)?)px$/);\n if (px) {\n const ptValue = (Number(px[1]) * 72) / 96;\n return Math.max(1, Math.round(ptValue * 2));\n }\n return undefined;\n}\n\nfunction normalizeFontFamily(value: string | undefined): string | undefined {\n if (!value) return undefined;\n const first = value.split(\",\")[0]?.trim();\n if (!first) return undefined;\n return first.replace(/^[\"']|[\"']$/g, \"\");\n}\n\nfunction mergeTextStyle(base: TextStyle, patch: TextStyle): TextStyle {\n return {\n bold: patch.bold ?? base.bold,\n italic: patch.italic ?? base.italic,\n underline: patch.underline ?? base.underline,\n colorHex: patch.colorHex ?? base.colorHex,\n fontFamily: patch.fontFamily ?? base.fontFamily,\n fontSizeHalfPoints: patch.fontSizeHalfPoints ?? base.fontSizeHalfPoints,\n };\n}\n\nfunction styleFromElement(node: HtmlNode): TextStyle {\n const tag = node.name?.toLowerCase();\n const styleAttr = node.attribs?.style;\n const css = parseStyleAttribute(styleAttr);\n\n const boldFromCss = (() => {\n const v = css[\"font-weight\"]?.trim().toLowerCase();\n if (!v) return undefined;\n if (v === \"bold\" || v === \"bolder\") return true;\n const n = Number(v);\n if (!Number.isNaN(n)) return n >= 600;\n return undefined;\n })();\n\n const italicFromCss = (() => {\n const v = css[\"font-style\"]?.trim().toLowerCase();\n if (!v) return undefined;\n if (v === \"italic\" || v === \"oblique\") return true;\n return undefined;\n })();\n\n const underlineFromCss = (() => {\n const v = css[\"text-decoration\"]?.trim().toLowerCase();\n if (!v) return undefined;\n return v.includes(\"underline\");\n })();\n\n const tagBold = tag === \"b\" || tag === \"strong\" ? true : undefined;\n const tagItalic = tag === \"i\" || tag === \"em\" ? true : undefined;\n const tagUnderline = tag === \"u\" ? true : undefined;\n\n return {\n bold: tagBold ?? boldFromCss,\n italic: tagItalic ?? italicFromCss,\n underline: tagUnderline ?? underlineFromCss,\n colorHex: parseCssColorToHex(css.color),\n fontFamily: normalizeFontFamily(css[\"font-family\"]),\n fontSizeHalfPoints: parseFontSizeToHalfPoints(css[\"font-size\"]),\n };\n}\n\nfunction getTextContent(node: HtmlNode): string {\n if (node.type === \"text\") return node.data ?? \"\";\n let out = \"\";\n const children = node.children ?? [];\n for (const c of children) out += getTextContent(c);\n return out;\n}\n\ntype RunToken =\n | { kind: \"text\"; text: string; style: TextStyle }\n | { kind: \"br\" }\n | { kind: \"image\"; image: EmbeddedImageRef };\n\ntype EmbeddedImage = {\n relationshipId: string;\n target: string;\n data: Uint8Array;\n contentType: string;\n widthPx: number;\n heightPx: number;\n};\n\ntype EmbeddedImageRef = {\n relationshipId: string;\n widthPx: number;\n heightPx: number;\n};\n\ntype HtmlToWordResult = {\n bodyXml: string;\n images: EmbeddedImage[];\n};\n\nfunction decodeBase64ToUint8Array(base64: string): Uint8Array {\n const BufferCtor = (globalThis as unknown as { Buffer?: typeof Buffer }).Buffer;\n if (BufferCtor) {\n return new Uint8Array(BufferCtor.from(base64, \"base64\"));\n }\n const atobFn = (globalThis as unknown as { atob?: (data: string) => string }).atob;\n if (!atobFn) {\n throw new Error(\"Base64 decode is not available in this environment.\");\n }\n const bin = atobFn(base64);\n const bytes = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);\n return bytes;\n}\n\nfunction parseImageDataUrl(\n src: string,\n): { contentType: string; data: Uint8Array; extension: \"png\" | \"jpeg\" } | undefined {\n const m = src.match(/^data:(image\\/png|image\\/jpeg);base64,([\\s\\S]+)$/i);\n if (!m) return undefined;\n const contentType = m[1].toLowerCase();\n const base64 = m[2].replace(/\\s+/g, \"\");\n const data = decodeBase64ToUint8Array(base64);\n const extension = contentType === \"image/png\" ? \"png\" : \"jpeg\";\n return { contentType, data, extension };\n}\n\nfunction parseCssLengthToPx(value: string | undefined): number | undefined {\n if (!value) return undefined;\n const v = value.trim().toLowerCase();\n const px = v.match(/^(\\d+(?:\\.\\d+)?)px$/);\n if (px) return Math.max(1, Math.round(Number(px[1])));\n return undefined;\n}\n\nfunction readUInt32BE(bytes: Uint8Array, offset: number): number | undefined {\n if (offset < 0 || offset + 4 > bytes.length) return undefined;\n return (\n ((bytes[offset] ?? 0) << 24) |\n ((bytes[offset + 1] ?? 0) << 16) |\n ((bytes[offset + 2] ?? 0) << 8) |\n (bytes[offset + 3] ?? 0)\n ) >>> 0;\n}\n\nfunction parsePngDimensions(data: Uint8Array): { widthPx: number; heightPx: number } | undefined {\n if (data.length < 24) return undefined;\n const signature = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n for (let i = 0; i < signature.length; i++) {\n if ((data[i] ?? 0) !== signature[i]) return undefined;\n }\n const widthPx = readUInt32BE(data, 16);\n const heightPx = readUInt32BE(data, 20);\n if (!widthPx || !heightPx) return undefined;\n return { widthPx, heightPx };\n}\n\nfunction parseJpegDimensions(data: Uint8Array): { widthPx: number; heightPx: number } | undefined {\n if (data.length < 4) return undefined;\n if (data[0] !== 0xff || data[1] !== 0xd8) return undefined;\n\n let offset = 2;\n while (offset + 4 <= data.length) {\n if (data[offset] !== 0xff) {\n offset++;\n continue;\n }\n while (offset < data.length && data[offset] === 0xff) offset++;\n if (offset >= data.length) return undefined;\n\n const marker = data[offset] as number;\n offset++;\n\n const isStandalone = marker === 0xd9 || marker === 0xda;\n if (isStandalone) break;\n if (offset + 2 > data.length) return undefined;\n const length = ((data[offset] as number) << 8) | (data[offset + 1] as number);\n if (length < 2 || offset + length > data.length) return undefined;\n\n const isSof =\n marker === 0xc0 ||\n marker === 0xc1 ||\n marker === 0xc2 ||\n marker === 0xc3 ||\n marker === 0xc5 ||\n marker === 0xc6 ||\n marker === 0xc7 ||\n marker === 0xc9 ||\n marker === 0xca ||\n marker === 0xcb ||\n marker === 0xcd ||\n marker === 0xce ||\n marker === 0xcf;\n if (isSof) {\n if (offset + 7 > data.length) return undefined;\n const heightPx = ((data[offset + 3] as number) << 8) | (data[offset + 4] as number);\n const widthPx = ((data[offset + 5] as number) << 8) | (data[offset + 6] as number);\n if (!widthPx || !heightPx) return undefined;\n return { widthPx, heightPx };\n }\n\n offset += length;\n }\n\n return undefined;\n}\n\nfunction parseIntrinsicImageSizePx(\n contentType: string,\n data: Uint8Array,\n): { widthPx: number; heightPx: number } | undefined {\n if (contentType === \"image/png\") return parsePngDimensions(data);\n if (contentType === \"image/jpeg\") return parseJpegDimensions(data);\n return undefined;\n}\n\nfunction applyMaxBoxPx(\n size: { widthPx: number; heightPx: number },\n maxBox: { maxWidthPx: number; maxHeightPx: number },\n): { widthPx: number; heightPx: number } {\n const w = Math.max(1, Math.round(size.widthPx));\n const h = Math.max(1, Math.round(size.heightPx));\n const scale = Math.min(1, maxBox.maxWidthPx / w, maxBox.maxHeightPx / h);\n return { widthPx: Math.max(1, Math.round(w * scale)), heightPx: Math.max(1, Math.round(h * scale)) };\n}\n\nfunction computeImageSizePx(\n node: HtmlNode,\n intrinsic: { widthPx: number; heightPx: number } | undefined,\n): { widthPx: number; heightPx: number } {\n const wAttr = node.attribs?.width ? Number(node.attribs.width) : undefined;\n const hAttr = node.attribs?.height ? Number(node.attribs.height) : undefined;\n const css = parseStyleAttribute(node.attribs?.style);\n const wCss = parseCssLengthToPx(css.width);\n const hCss = parseCssLengthToPx(css.height);\n\n const widthAttrPx = Number.isFinite(wAttr) && wAttr ? Math.max(1, Math.round(wAttr)) : undefined;\n const heightAttrPx = Number.isFinite(hAttr) && hAttr ? Math.max(1, Math.round(hAttr)) : undefined;\n\n const ratio =\n intrinsic && intrinsic.widthPx > 0 && intrinsic.heightPx > 0\n ? intrinsic.heightPx / intrinsic.widthPx\n : widthAttrPx && heightAttrPx\n ? heightAttrPx / widthAttrPx\n : 0.5;\n\n const widthPx =\n typeof wCss === \"number\"\n ? wCss\n : typeof widthAttrPx === \"number\"\n ? widthAttrPx\n : intrinsic?.widthPx ?? 300;\n const heightPx =\n typeof hCss === \"number\"\n ? hCss\n : typeof heightAttrPx === \"number\"\n ? heightAttrPx\n : intrinsic?.heightPx ?? 150;\n\n const finalSize =\n typeof wCss === \"number\" && typeof hCss !== \"number\"\n ? { widthPx, heightPx: Math.max(1, Math.round(widthPx * ratio)) }\n : typeof hCss === \"number\" && typeof wCss !== \"number\"\n ? { widthPx: Math.max(1, Math.round(heightPx / ratio)), heightPx }\n : typeof widthAttrPx === \"number\" && typeof heightAttrPx !== \"number\" && intrinsic\n ? { widthPx, heightPx: Math.max(1, Math.round(widthPx * ratio)) }\n : typeof heightAttrPx === \"number\" && typeof widthAttrPx !== \"number\" && intrinsic\n ? { widthPx: Math.max(1, Math.round(heightPx / ratio)), heightPx }\n : { widthPx, heightPx };\n\n return applyMaxBoxPx(finalSize, { maxWidthPx: 624, maxHeightPx: 864 });\n}\n\nfunction collectInlineRuns(\n node: HtmlNode,\n inherited: TextStyle,\n out: RunToken[],\n result: HtmlToWordResult,\n): void {\n if (node.type === \"text\") {\n const text = node.data ?? \"\";\n if (text) out.push({ kind: \"text\", text, style: inherited });\n return;\n }\n\n if (node.type === \"tag\") {\n const tag = node.name?.toLowerCase();\n if (tag === \"br\") {\n out.push({ kind: \"br\" });\n return;\n }\n if (tag === \"img\") {\n const src = node.attribs?.src;\n if (!src) return;\n const parsed = parseImageDataUrl(src);\n if (!parsed) return;\n const intrinsic = parseIntrinsicImageSizePx(parsed.contentType, parsed.data);\n const { widthPx, heightPx } = computeImageSizePx(node, intrinsic);\n const id = result.images.length + 1;\n const relationshipId = `rId${id}`;\n const target = `media/image${id}.${parsed.extension}`;\n result.images.push({\n relationshipId,\n target,\n data: parsed.data,\n contentType: parsed.contentType,\n widthPx,\n heightPx,\n });\n out.push({ kind: \"image\", image: { relationshipId, widthPx, heightPx } });\n return;\n }\n if (tag === \"canvas\") {\n const dataUrl = node.attribs?.[\"data-image\"] ?? node.attribs?.[\"data-src\"];\n if (!dataUrl) return;\n const parsed = parseImageDataUrl(dataUrl);\n if (!parsed) return;\n const bufferW = node.attribs?.width ? Number(node.attribs.width) : undefined;\n const bufferH = node.attribs?.height ? Number(node.attribs.height) : undefined;\n const intrinsic =\n Number.isFinite(bufferW) && bufferW && Number.isFinite(bufferH) && bufferH\n ? { widthPx: Math.max(1, Math.round(bufferW)), heightPx: Math.max(1, Math.round(bufferH)) }\n : parseIntrinsicImageSizePx(parsed.contentType, parsed.data);\n const { widthPx, heightPx } = computeImageSizePx(node, intrinsic);\n const id = result.images.length + 1;\n const relationshipId = `rId${id}`;\n const target = `media/image${id}.${parsed.extension}`;\n result.images.push({\n relationshipId,\n target,\n data: parsed.data,\n contentType: parsed.contentType,\n widthPx,\n heightPx,\n });\n out.push({ kind: \"image\", image: { relationshipId, widthPx, heightPx } });\n return;\n }\n const next = mergeTextStyle(inherited, styleFromElement(node));\n const children = node.children ?? [];\n for (const c of children) collectInlineRuns(c, next, out, result);\n return;\n }\n\n const children = node.children ?? [];\n for (const c of children) collectInlineRuns(c, inherited, out, result);\n}\n\nfunction buildRunXml(style: TextStyle, text: string): string {\n const rPrParts: string[] = [];\n if (style.bold) rPrParts.push(\"<w:b/>\");\n if (style.italic) rPrParts.push(\"<w:i/>\");\n if (style.underline) rPrParts.push('<w:u w:val=\"single\"/>');\n if (style.colorHex) rPrParts.push(`<w:color w:val=\"${style.colorHex}\"/>`);\n if (style.fontFamily) {\n const ff = escapeXmlText(style.fontFamily);\n rPrParts.push(`<w:rFonts w:ascii=\"${ff}\" w:hAnsi=\"${ff}\" w:eastAsia=\"${ff}\"/>`);\n }\n if (typeof style.fontSizeHalfPoints === \"number\") {\n const sz = style.fontSizeHalfPoints;\n rPrParts.push(`<w:sz w:val=\"${sz}\"/><w:szCs w:val=\"${sz}\"/>`);\n }\n\n const rPrXml = rPrParts.length ? `<w:rPr>${rPrParts.join(\"\")}</w:rPr>` : \"\";\n const escaped = escapeXmlText(text);\n const preserve = shouldPreserveSpace(text) ? ' xml:space=\"preserve\"' : \"\";\n return `<w:r>${rPrXml}<w:t${preserve}>${escaped}</w:t></w:r>`;\n}\n\nfunction pxToEmu(px: number): number {\n return Math.max(1, Math.round(px * 9525));\n}\n\nfunction buildImageRunXml(image: EmbeddedImageRef): string {\n const cx = pxToEmu(image.widthPx);\n const cy = pxToEmu(image.heightPx);\n const docPrId = image.relationshipId.replace(/^rId/, \"\");\n const name = `Picture ${docPrId}`;\n\n return `<w:r><w:drawing xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\" xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\"><wp:inline distT=\"0\" distB=\"0\" distL=\"0\" distR=\"0\"><wp:extent cx=\"${cx}\" cy=\"${cy}\"/><wp:docPr id=\"${docPrId}\" name=\"${escapeXmlText(name)}\"/><a:graphic><a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\"><pic:pic><pic:nvPicPr><pic:cNvPr id=\"0\" name=\"${escapeXmlText(name)}\"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed=\"${image.relationshipId}\"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x=\"0\" y=\"0\"/><a:ext cx=\"${cx}\" cy=\"${cy}\"/></a:xfrm><a:prstGeom prst=\"rect\"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing></w:r>`;\n}\n\nfunction hasClass(node: HtmlNode, className: string): boolean {\n const cls = node.attribs?.class;\n if (!cls) return false;\n return cls.split(/\\s+/).includes(className);\n}\n\nfunction isSkippableSubtree(node: HtmlNode): boolean {\n if (node.type !== \"tag\") return false;\n const tag = node.name?.toLowerCase();\n if (tag === \"button\") return true;\n if (tag === \"canvas\") {\n const dataUrl = node.attribs?.[\"data-image\"] ?? node.attribs?.[\"data-src\"];\n if (!dataUrl) return true;\n }\n if (tag === \"img\" && hasClass(node, \"ProseMirror-separator\")) return true;\n if (node.attribs?.id === \"pages\") return true;\n if (hasClass(node, \"ProseMirror-widget\")) return true;\n return false;\n}\n\nfunction parseCssLengthToTwips(\n value: string | undefined,\n baseFontHalfPoints: number,\n): number | undefined {\n if (!value) return undefined;\n const v = value.trim().toLowerCase();\n if (!v) return undefined;\n\n const pt = v.match(/^(-?\\d+(?:\\.\\d+)?)pt$/);\n if (pt) return Math.round(Number(pt[1]) * 20);\n\n const px = v.match(/^(-?\\d+(?:\\.\\d+)?)px$/);\n if (px) return Math.round((Number(px[1]) * 72 * 20) / 96);\n\n const em = v.match(/^(-?\\d+(?:\\.\\d+)?)em$/);\n if (em) {\n const basePt = baseFontHalfPoints / 2;\n return Math.round(Number(em[1]) * basePt * 20);\n }\n\n const num = v.match(/^(-?\\d+(?:\\.\\d+)?)$/);\n if (num) return Math.round(Number(num[1]));\n\n return undefined;\n}\n\nfunction inferFirstFontSizeHalfPoints(node: HtmlNode): number | undefined {\n const stack: HtmlNode[] = [node];\n while (stack.length) {\n const cur = stack.pop() as HtmlNode;\n if (cur.type === \"tag\") {\n const css = parseStyleAttribute(cur.attribs?.style);\n const sz = parseFontSizeToHalfPoints(css[\"font-size\"]);\n if (typeof sz === \"number\") return sz;\n }\n const children = cur.children ?? [];\n for (let i = children.length - 1; i >= 0; i--) {\n stack.push(children[i] as HtmlNode);\n }\n }\n return undefined;\n}\n\nfunction buildParagraphPrXml(\n node: HtmlNode,\n baseFontHalfPoints: number,\n extraInd?: { leftTwips?: number; hangingTwips?: number },\n): string {\n const css = parseStyleAttribute(node.attribs?.style);\n const parts: string[] = [];\n\n const align = css[\"text-align\"]?.trim().toLowerCase();\n const jcVal =\n align === \"center\"\n ? \"center\"\n : align === \"right\"\n ? \"right\"\n : align === \"justify\"\n ? \"both\"\n : undefined;\n if (jcVal) parts.push(`<w:jc w:val=\"${jcVal}\"/>`);\n\n const left = (() => {\n const marginLeft = parseCssLengthToTwips(css[\"margin-left\"], baseFontHalfPoints);\n const paddingLeft = parseCssLengthToTwips(css[\"padding-left\"], baseFontHalfPoints);\n const sum = (marginLeft ?? 0) + (paddingLeft ?? 0);\n if (!sum) return undefined;\n return Math.max(0, sum);\n })();\n\n const firstLine = (() => {\n const textIndent = parseCssLengthToTwips(css[\"text-indent\"], baseFontHalfPoints);\n if (typeof textIndent !== \"number\" || !textIndent) return undefined;\n return Math.max(0, textIndent);\n })();\n\n const indAttrs: string[] = [];\n const leftTwips = extraInd?.leftTwips ?? left;\n if (typeof leftTwips === \"number\") indAttrs.push(`w:left=\"${leftTwips}\"`);\n const hangingTwips = extraInd?.hangingTwips;\n if (typeof hangingTwips === \"number\") indAttrs.push(`w:hanging=\"${hangingTwips}\"`);\n if (typeof firstLine === \"number\") indAttrs.push(`w:firstLine=\"${firstLine}\"`);\n if (indAttrs.length) parts.push(`<w:ind ${indAttrs.join(\" \")}/>`);\n\n const before = parseCssLengthToTwips(css[\"margin-top\"], baseFontHalfPoints);\n const after = parseCssLengthToTwips(css[\"margin-bottom\"], baseFontHalfPoints);\n const lineHeight = (() => {\n const lh = css[\"line-height\"]?.trim().toLowerCase();\n if (!lh || lh === \"normal\") return undefined;\n\n const unitless = lh.match(/^(\\d+(?:\\.\\d+)?)$/);\n if (unitless) {\n const multiplier = Number(unitless[1]);\n if (!Number.isFinite(multiplier) || multiplier <= 0) return undefined;\n const basePt = baseFontHalfPoints / 2;\n return Math.round(basePt * multiplier * 20);\n }\n\n const twips = parseCssLengthToTwips(lh, baseFontHalfPoints);\n if (typeof twips !== \"number\") return undefined;\n return Math.max(1, twips);\n })();\n\n if (\n typeof before === \"number\" ||\n typeof after === \"number\" ||\n typeof lineHeight === \"number\"\n ) {\n const attrs: string[] = [];\n if (typeof before === \"number\") attrs.push(`w:before=\"${Math.max(0, before)}\"`);\n if (typeof after === \"number\") attrs.push(`w:after=\"${Math.max(0, after)}\"`);\n if (typeof lineHeight === \"number\") {\n attrs.push(`w:line=\"${lineHeight}\"`, 'w:lineRule=\"exact\"');\n }\n parts.push(`<w:spacing ${attrs.join(\" \")}/>`);\n }\n\n if (!parts.length) return \"\";\n return `<w:pPr>${parts.join(\"\")}</w:pPr>`;\n}\n\nfunction buildParagraphXmlFromContainer(\n node: HtmlNode,\n baseStyle: TextStyle,\n extraInd?: { leftTwips?: number; hangingTwips?: number },\n result?: HtmlToWordResult,\n): string {\n const baseFontHalfPoints = baseStyle.fontSizeHalfPoints ?? inferFirstFontSizeHalfPoints(node) ?? 28;\n const pPrXml = buildParagraphPrXml(node, baseFontHalfPoints, extraInd);\n\n const runs: RunToken[] = [];\n const res =\n result ??\n ({\n bodyXml: \"\",\n images: [],\n } as HtmlToWordResult);\n for (const c of node.children ?? []) collectInlineRuns(c, baseStyle, runs, res);\n\n const rXml: string[] = [];\n for (const token of runs) {\n if (token.kind === \"br\") {\n rXml.push(\"<w:r><w:br/></w:r>\");\n continue;\n }\n if (token.kind === \"image\") {\n rXml.push(buildImageRunXml(token.image));\n continue;\n }\n const text = token.text;\n if (!text) continue;\n if (!text.trim()) continue;\n rXml.push(buildRunXml(token.style, text));\n }\n\n if (!rXml.length) return \"\";\n return `<w:p>${pPrXml}${rXml.join(\"\")}</w:p>`;\n}\n\nconst PAGE_BREAK_XML = '<w:p><w:r><w:br w:type=\"page\"/></w:r></w:p>';\n\nfunction isExplicitPageBreak(node: HtmlNode): boolean {\n if (node.type !== \"tag\") return false;\n const tag = node.name?.toLowerCase();\n const css = parseStyleAttribute(node.attribs?.style);\n const cls = node.attribs?.class ?? \"\";\n const classList = cls ? cls.split(/\\s+/) : [];\n\n if (tag === \"hr\" && classList.includes(\"page-break\")) return true;\n if (classList.includes(\"page-break\")) return true;\n if (node.attribs?.[\"data-page-break\"] === \"true\") return true;\n\n const after = css[\"page-break-after\"]?.toLowerCase() ?? css[\"break-after\"]?.toLowerCase();\n const before = css[\"page-break-before\"]?.toLowerCase() ?? css[\"break-before\"]?.toLowerCase();\n if (after?.includes(\"always\") || before?.includes(\"always\")) return true;\n\n return false;\n}\n\nfunction buildHeadingBaseStyle(level: number): TextStyle {\n const size = level === 1 ? 44 : level === 2 ? 32 : level === 3 ? 28 : level === 4 ? 24 : 22;\n return { bold: true, fontSizeHalfPoints: size };\n}\n\nfunction buildListBlocks(listNode: HtmlNode, ordered: boolean, result: HtmlToWordResult): string[] {\n const items: HtmlNode[] = [];\n const stack: HtmlNode[] = [...(listNode.children ?? [])];\n while (stack.length) {\n const n = stack.shift() as HtmlNode;\n if (n.type === \"tag\" && n.name?.toLowerCase() === \"li\") items.push(n);\n }\n\n const out: string[] = [];\n for (let i = 0; i < items.length; i++) {\n const prefix = ordered ? `${i + 1}. ` : \"• \";\n const li = items[i] as HtmlNode;\n\n const baseStyle: TextStyle = {};\n const runs: RunToken[] = [];\n runs.push({ kind: \"text\", text: prefix, style: baseStyle });\n for (const c of li.children ?? []) collectInlineRuns(c, baseStyle, runs, result);\n\n const rXml: string[] = [];\n for (const token of runs) {\n if (token.kind === \"br\") {\n rXml.push(\"<w:r><w:br/></w:r>\");\n continue;\n }\n if (token.kind === \"image\") {\n rXml.push(buildImageRunXml(token.image));\n continue;\n }\n const text = token.text;\n if (!text) continue;\n if (!text.trim()) continue;\n rXml.push(buildRunXml(token.style, text));\n }\n if (!rXml.length) continue;\n\n const pPrXml = buildParagraphPrXml(li, inferFirstFontSizeHalfPoints(li) ?? 28, {\n leftTwips: 720,\n hangingTwips: 360,\n });\n out.push(`<w:p>${pPrXml}${rXml.join(\"\")}</w:p>`);\n }\n\n return out;\n}\n\nfunction buildTableXml(tableNode: HtmlNode, result: HtmlToWordResult): string {\n const rows: HtmlNode[] = [];\n const stack: HtmlNode[] = [...(tableNode.children ?? [])];\n while (stack.length) {\n const n = stack.shift() as HtmlNode;\n if (n.type === \"tag\" && n.name?.toLowerCase() === \"tr\") rows.push(n);\n if (n.children?.length) stack.unshift(...n.children);\n }\n\n const rowXml: string[] = [];\n for (const tr of rows) {\n const cells = (tr.children ?? []).filter(\n (c) => c.type === \"tag\" && (c.name === \"td\" || c.name === \"th\"),\n );\n\n const cellXml: string[] = [];\n for (const cell of cells) {\n const isHeader = cell.name === \"th\";\n const baseStyle: TextStyle = isHeader ? { bold: true } : {};\n const pXml = buildParagraphXmlFromContainer(cell, baseStyle, undefined, result);\n const paragraphs = pXml ? pXml : \"<w:p/>\";\n cellXml.push(\n `<w:tc><w:tcPr><w:tcW w:w=\"0\" w:type=\"auto\"/></w:tcPr>${paragraphs}</w:tc>`,\n );\n }\n if (cellXml.length) rowXml.push(`<w:tr>${cellXml.join(\"\")}</w:tr>`);\n }\n\n const tblPr = `<w:tblPr><w:tblW w:w=\"0\" w:type=\"auto\"/><w:tblBorders><w:top w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"D9D9D9\"/><w:left w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"D9D9D9\"/><w:bottom w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"D9D9D9\"/><w:right w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"D9D9D9\"/><w:insideH w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"D9D9D9\"/><w:insideV w:val=\"single\" w:sz=\"4\" w:space=\"0\" w:color=\"D9D9D9\"/></w:tblBorders></w:tblPr>`;\n const tblGrid = `<w:tblGrid/>`;\n return `<w:tbl>${tblPr}${tblGrid}${rowXml.join(\"\")}</w:tbl>`;\n}\n\nfunction buildParagraphXmlFromSingleInlineNode(\n node: HtmlNode,\n baseStyle: TextStyle,\n result: HtmlToWordResult,\n): string {\n const wrapper: HtmlNode = {\n type: \"tag\",\n name: \"p\",\n attribs: { style: \"text-align: center;\" },\n children: [node],\n };\n return buildParagraphXmlFromContainer(wrapper, baseStyle, undefined, result);\n}\n\nfunction collectBodyBlocks(node: HtmlNode, out: string[], result: HtmlToWordResult): void {\n if (isSkippableSubtree(node)) return;\n\n if (node.type === \"tag\") {\n const tag = node.name?.toLowerCase();\n\n if (isExplicitPageBreak(node)) {\n out.push(PAGE_BREAK_XML);\n return;\n }\n\n if (tag === \"p\") {\n const pXml = buildParagraphXmlFromContainer(node, {}, undefined, result);\n if (pXml) out.push(pXml);\n return;\n }\n\n if (tag === \"img\" || tag === \"canvas\") {\n const pXml = buildParagraphXmlFromSingleInlineNode(node, {}, result);\n if (pXml) out.push(pXml);\n return;\n }\n\n if (tag && /^h[1-6]$/.test(tag)) {\n const level = Number(tag.slice(1));\n const hXml = buildParagraphXmlFromContainer(node, buildHeadingBaseStyle(level), undefined, result);\n if (hXml) out.push(hXml);\n return;\n }\n\n if (tag === \"table\") {\n const tblXml = buildTableXml(node, result);\n if (tblXml) out.push(tblXml);\n return;\n }\n\n if (tag === \"ul\" || tag === \"ol\") {\n out.push(...buildListBlocks(node, tag === \"ol\", result));\n return;\n }\n }\n\n for (const c of node.children ?? []) collectBodyBlocks(c, out, result);\n}\n\nexport function textToWordBodyXml(text: string): string {\n const normalized = text.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n if (!normalized.trim()) {\n throw new Error(\"Text is empty.\");\n }\n\n const lines = normalized.split(\"\\n\");\n const out: string[] = [];\n for (const line of lines) {\n if (!line) {\n out.push(\"<w:p/>\");\n continue;\n }\n out.push(`<w:p>${buildRunXml({}, line)}</w:p>`);\n }\n return out.join(\"\");\n}\n\nfunction htmlToWordBody(html: string): HtmlToWordResult {\n const normalized = html.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n const doc = parseDocument(normalized, {\n lowerCaseAttributeNames: true,\n lowerCaseTags: true,\n recognizeSelfClosing: true,\n }) as unknown as HtmlNode;\n\n const result: HtmlToWordResult = { bodyXml: \"\", images: [] };\n const out: string[] = [];\n collectBodyBlocks(doc, out, result);\n result.bodyXml = out.join(\"\");\n return result;\n}\n\n/**\n * 把 HTML 字符串转换成 WordprocessingML 的 body 片段(由 <w:p> / <w:tbl> 等组成)。\n * 说明:\n * - 这个函数只生成 body 内容,不生成完整的 <w:document> 包装\n * - 支持:p/span/strong/br、h1~h6、ul/ol/li、table/tr/td/th、基础分页标记\n * - 不支持:canvas 图表、复杂 CSS、HTML 图片(需要额外提供图片二进制)\n */\nexport function htmlToWordBodyXml(html: string): string {\n const { bodyXml } = htmlToWordBody(html);\n if (!bodyXml) {\n const text = getTextContent(\n parseDocument(html, {\n lowerCaseAttributeNames: true,\n lowerCaseTags: true,\n recognizeSelfClosing: true,\n }) as unknown as HtmlNode,\n );\n return textToWordBodyXml(text);\n }\n return bodyXml;\n}\n\nexport function htmlToWordBodyWithAssets(html: string): HtmlToWordResult {\n const result = htmlToWordBody(html);\n if (!result.bodyXml) {\n const text = getTextContent(\n parseDocument(html, {\n lowerCaseAttributeNames: true,\n lowerCaseTags: true,\n recognizeSelfClosing: true,\n }) as unknown as HtmlNode,\n );\n return { bodyXml: textToWordBodyXml(text), images: [] };\n }\n return result;\n}\n"],"mappings":"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","names":["children"]}
|