@yinyoudexing/xml2word 0.1.1 → 0.1.3
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-BWHSZ7VQ.js +500 -0
- package/dist/createDocxZip-BWHSZ7VQ.js.map +1 -0
- package/dist/{htmlToWordBodyXml-AG3GTZEZ.js → htmlToWordBodyXml-SIVUZ7K7.js} +381 -49
- package/dist/htmlToWordBodyXml-SIVUZ7K7.js.map +1 -0
- package/dist/index.cjs +736 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -3
- package/package.json +1 -1
- package/dist/createDocxZip-WGQSPGIF.js +0 -152
- package/dist/createDocxZip-WGQSPGIF.js.map +0 -1
- package/dist/htmlToWordBodyXml-AG3GTZEZ.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -48,6 +48,8 @@ function wrapBodyXml(bodyXml) {
|
|
|
48
48
|
<w:body>
|
|
49
49
|
${bodyXml}
|
|
50
50
|
<w:sectPr>
|
|
51
|
+
<w:headerReference w:type="default" r:id="rId6"/>
|
|
52
|
+
<w:footerReference w:type="default" r:id="rId7"/>
|
|
51
53
|
<w:pgSz w:w="12240" w:h="15840"/>
|
|
52
54
|
<w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="708" w:footer="708" w:gutter="0"/>
|
|
53
55
|
<w:cols w:space="708"/>
|
|
@@ -136,17 +138,36 @@ function buildContentTypesXml(assets) {
|
|
|
136
138
|
defaults.set(ext, asset.contentType);
|
|
137
139
|
}
|
|
138
140
|
const defaultLines = [...defaults.entries()].sort((a, b) => a[0].localeCompare(b[0])).map(([ext, ct]) => ` <Default Extension="${ext}" ContentType="${ct}"/>`).join("\n");
|
|
141
|
+
const overrides = [
|
|
142
|
+
' <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>',
|
|
143
|
+
' <Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>',
|
|
144
|
+
' <Override PartName="/word/settings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"/>',
|
|
145
|
+
' <Override PartName="/word/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>',
|
|
146
|
+
' <Override PartName="/word/fontTable.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"/>',
|
|
147
|
+
' <Override PartName="/word/numbering.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"/>',
|
|
148
|
+
' <Override PartName="/word/header1.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"/>',
|
|
149
|
+
' <Override PartName="/word/footer1.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"/>'
|
|
150
|
+
].join("\n");
|
|
139
151
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
140
152
|
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
141
153
|
${defaultLines}
|
|
142
|
-
|
|
154
|
+
${overrides}
|
|
143
155
|
</Types>
|
|
144
156
|
`;
|
|
145
157
|
}
|
|
146
158
|
function buildDocumentRelsXml(assets) {
|
|
147
|
-
const relLines =
|
|
148
|
-
|
|
149
|
-
|
|
159
|
+
const relLines = [
|
|
160
|
+
' <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>',
|
|
161
|
+
' <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/>',
|
|
162
|
+
' <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/>',
|
|
163
|
+
' <Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml"/>',
|
|
164
|
+
' <Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" Target="numbering.xml"/>',
|
|
165
|
+
' <Relationship Id="rId6" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/header" Target="header1.xml"/>',
|
|
166
|
+
' <Relationship Id="rId7" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer" Target="footer1.xml"/>',
|
|
167
|
+
...assets.map(
|
|
168
|
+
(a) => ` <Relationship Id="${a.relationshipId}" Type="${a.relationshipType}" Target="${a.target}"/>`
|
|
169
|
+
)
|
|
170
|
+
].join("\n");
|
|
150
171
|
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
151
172
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
152
173
|
${relLines}
|
|
@@ -162,6 +183,15 @@ async function createDocxZipUint8Array(xml, options = {}) {
|
|
|
162
183
|
relsFolder?.file(".rels", ROOT_RELS_XML);
|
|
163
184
|
const wordFolder = zip.folder("word");
|
|
164
185
|
wordFolder?.file("document.xml", documentXml);
|
|
186
|
+
wordFolder?.file("styles.xml", STYLES_XML);
|
|
187
|
+
wordFolder?.file("settings.xml", SETTINGS_XML);
|
|
188
|
+
wordFolder?.file("fontTable.xml", FONT_TABLE_XML);
|
|
189
|
+
wordFolder?.folder("theme")?.file("theme1.xml", THEME_XML);
|
|
190
|
+
wordFolder?.file("numbering.xml", NUMBERING_XML);
|
|
191
|
+
wordFolder?.file("header1.xml", HEADER1_XML);
|
|
192
|
+
wordFolder?.file("footer1.xml", FOOTER1_XML);
|
|
193
|
+
const wordRelsFolder = wordFolder?.folder("_rels");
|
|
194
|
+
wordRelsFolder?.file("document.xml.rels", buildDocumentRelsXml([]));
|
|
165
195
|
return zip.generateAsync({ type: "uint8array" });
|
|
166
196
|
}
|
|
167
197
|
async function createDocxZipWithAssetsUint8Array(xml, options, assets) {
|
|
@@ -173,9 +203,16 @@ async function createDocxZipWithAssetsUint8Array(xml, options, assets) {
|
|
|
173
203
|
relsFolder?.file(".rels", ROOT_RELS_XML);
|
|
174
204
|
const wordFolder = zip.folder("word");
|
|
175
205
|
wordFolder?.file("document.xml", documentXml);
|
|
206
|
+
wordFolder?.file("styles.xml", STYLES_XML);
|
|
207
|
+
wordFolder?.file("settings.xml", SETTINGS_XML);
|
|
208
|
+
wordFolder?.file("fontTable.xml", FONT_TABLE_XML);
|
|
209
|
+
wordFolder?.folder("theme")?.file("theme1.xml", THEME_XML);
|
|
210
|
+
wordFolder?.file("numbering.xml", NUMBERING_XML);
|
|
211
|
+
wordFolder?.file("header1.xml", HEADER1_XML);
|
|
212
|
+
wordFolder?.file("footer1.xml", FOOTER1_XML);
|
|
213
|
+
const wordRelsFolder = wordFolder?.folder("_rels");
|
|
214
|
+
wordRelsFolder?.file("document.xml.rels", buildDocumentRelsXml(assets));
|
|
176
215
|
if (assets.length) {
|
|
177
|
-
const wordRelsFolder = wordFolder?.folder("_rels");
|
|
178
|
-
wordRelsFolder?.file("document.xml.rels", buildDocumentRelsXml(assets));
|
|
179
216
|
for (const asset of assets) {
|
|
180
217
|
const targetPath = asset.target.replace(/^\.\//, "");
|
|
181
218
|
const normalized = targetPath.startsWith("word/") ? targetPath.slice("word/".length) : targetPath;
|
|
@@ -184,13 +221,324 @@ async function createDocxZipWithAssetsUint8Array(xml, options, assets) {
|
|
|
184
221
|
}
|
|
185
222
|
return zip.generateAsync({ type: "uint8array" });
|
|
186
223
|
}
|
|
187
|
-
var import_jszip, ROOT_RELS_XML;
|
|
224
|
+
var import_jszip, STYLES_XML, SETTINGS_XML, NUMBERING_XML, HEADER1_XML, FOOTER1_XML, THEME_XML, FONT_TABLE_XML, ROOT_RELS_XML;
|
|
188
225
|
var init_createDocxZip = __esm({
|
|
189
226
|
"src/lib/createDocxZip.ts"() {
|
|
190
227
|
"use strict";
|
|
191
228
|
import_jszip = __toESM(require("jszip"), 1);
|
|
192
229
|
init_normalizeDocumentXml();
|
|
193
230
|
init_validateXml();
|
|
231
|
+
STYLES_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
232
|
+
<w:styles xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
233
|
+
<w:docDefaults>
|
|
234
|
+
<w:rPrDefault>
|
|
235
|
+
<w:rPr>
|
|
236
|
+
<w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman" w:eastAsia="FangSong_GB2312" w:cs="Times New Roman"/>
|
|
237
|
+
<w:sz w:val="28"/>
|
|
238
|
+
<w:szCs w:val="28"/>
|
|
239
|
+
<w:lang w:val="en-US" w:eastAsia="zh-CN"/>
|
|
240
|
+
</w:rPr>
|
|
241
|
+
</w:rPrDefault>
|
|
242
|
+
<w:pPrDefault>
|
|
243
|
+
<w:pPr>
|
|
244
|
+
<w:spacing w:before="0" w:after="160" w:line="360" w:lineRule="auto"/>
|
|
245
|
+
</w:pPr>
|
|
246
|
+
</w:pPrDefault>
|
|
247
|
+
</w:docDefaults>
|
|
248
|
+
|
|
249
|
+
<w:style w:type="paragraph" w:default="1" w:styleId="Normal">
|
|
250
|
+
<w:name w:val="Normal"/>
|
|
251
|
+
<w:qFormat/>
|
|
252
|
+
<w:pPr>
|
|
253
|
+
<w:spacing w:before="0" w:after="160" w:line="360" w:lineRule="auto"/>
|
|
254
|
+
</w:pPr>
|
|
255
|
+
<w:rPr>
|
|
256
|
+
<w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman" w:eastAsia="FangSong_GB2312" w:cs="Times New Roman"/>
|
|
257
|
+
<w:sz w:val="28"/>
|
|
258
|
+
<w:szCs w:val="28"/>
|
|
259
|
+
</w:rPr>
|
|
260
|
+
</w:style>
|
|
261
|
+
|
|
262
|
+
<w:style w:type="paragraph" w:styleId="Heading1">
|
|
263
|
+
<w:name w:val="heading 1"/>
|
|
264
|
+
<w:basedOn w:val="Normal"/>
|
|
265
|
+
<w:next w:val="Normal"/>
|
|
266
|
+
<w:uiPriority w:val="9"/>
|
|
267
|
+
<w:qFormat/>
|
|
268
|
+
<w:pPr>
|
|
269
|
+
<w:keepNext/>
|
|
270
|
+
<w:keepLines/>
|
|
271
|
+
<w:outlineLvl w:val="0"/>
|
|
272
|
+
<w:spacing w:before="240" w:after="120" w:line="360" w:lineRule="auto"/>
|
|
273
|
+
</w:pPr>
|
|
274
|
+
<w:rPr>
|
|
275
|
+
<w:b/>
|
|
276
|
+
<w:sz w:val="44"/>
|
|
277
|
+
<w:szCs w:val="44"/>
|
|
278
|
+
</w:rPr>
|
|
279
|
+
</w:style>
|
|
280
|
+
|
|
281
|
+
<w:style w:type="paragraph" w:styleId="Heading2">
|
|
282
|
+
<w:name w:val="heading 2"/>
|
|
283
|
+
<w:basedOn w:val="Normal"/>
|
|
284
|
+
<w:next w:val="Normal"/>
|
|
285
|
+
<w:uiPriority w:val="9"/>
|
|
286
|
+
<w:qFormat/>
|
|
287
|
+
<w:pPr>
|
|
288
|
+
<w:keepNext/>
|
|
289
|
+
<w:keepLines/>
|
|
290
|
+
<w:outlineLvl w:val="1"/>
|
|
291
|
+
<w:spacing w:before="200" w:after="100" w:line="360" w:lineRule="auto"/>
|
|
292
|
+
</w:pPr>
|
|
293
|
+
<w:rPr>
|
|
294
|
+
<w:b/>
|
|
295
|
+
<w:sz w:val="32"/>
|
|
296
|
+
<w:szCs w:val="32"/>
|
|
297
|
+
</w:rPr>
|
|
298
|
+
</w:style>
|
|
299
|
+
|
|
300
|
+
<w:style w:type="paragraph" w:styleId="Heading3">
|
|
301
|
+
<w:name w:val="heading 3"/>
|
|
302
|
+
<w:basedOn w:val="Normal"/>
|
|
303
|
+
<w:next w:val="Normal"/>
|
|
304
|
+
<w:uiPriority w:val="9"/>
|
|
305
|
+
<w:qFormat/>
|
|
306
|
+
<w:pPr>
|
|
307
|
+
<w:keepNext/>
|
|
308
|
+
<w:keepLines/>
|
|
309
|
+
<w:outlineLvl w:val="2"/>
|
|
310
|
+
<w:spacing w:before="180" w:after="90" w:line="360" w:lineRule="auto"/>
|
|
311
|
+
</w:pPr>
|
|
312
|
+
<w:rPr>
|
|
313
|
+
<w:b/>
|
|
314
|
+
<w:sz w:val="28"/>
|
|
315
|
+
<w:szCs w:val="28"/>
|
|
316
|
+
</w:rPr>
|
|
317
|
+
</w:style>
|
|
318
|
+
|
|
319
|
+
<w:style w:type="paragraph" w:styleId="Heading4">
|
|
320
|
+
<w:name w:val="heading 4"/>
|
|
321
|
+
<w:basedOn w:val="Normal"/>
|
|
322
|
+
<w:next w:val="Normal"/>
|
|
323
|
+
<w:uiPriority w:val="9"/>
|
|
324
|
+
<w:qFormat/>
|
|
325
|
+
<w:pPr>
|
|
326
|
+
<w:keepNext/>
|
|
327
|
+
<w:keepLines/>
|
|
328
|
+
<w:outlineLvl w:val="3"/>
|
|
329
|
+
<w:spacing w:before="160" w:after="80" w:line="360" w:lineRule="auto"/>
|
|
330
|
+
</w:pPr>
|
|
331
|
+
<w:rPr>
|
|
332
|
+
<w:b/>
|
|
333
|
+
<w:sz w:val="24"/>
|
|
334
|
+
<w:szCs w:val="24"/>
|
|
335
|
+
</w:rPr>
|
|
336
|
+
</w:style>
|
|
337
|
+
|
|
338
|
+
<w:style w:type="paragraph" w:styleId="Heading5">
|
|
339
|
+
<w:name w:val="heading 5"/>
|
|
340
|
+
<w:basedOn w:val="Normal"/>
|
|
341
|
+
<w:next w:val="Normal"/>
|
|
342
|
+
<w:uiPriority w:val="9"/>
|
|
343
|
+
<w:qFormat/>
|
|
344
|
+
<w:pPr>
|
|
345
|
+
<w:keepNext/>
|
|
346
|
+
<w:keepLines/>
|
|
347
|
+
<w:outlineLvl w:val="4"/>
|
|
348
|
+
<w:spacing w:before="140" w:after="70" w:line="360" w:lineRule="auto"/>
|
|
349
|
+
</w:pPr>
|
|
350
|
+
<w:rPr>
|
|
351
|
+
<w:b/>
|
|
352
|
+
<w:sz w:val="22"/>
|
|
353
|
+
<w:szCs w:val="22"/>
|
|
354
|
+
</w:rPr>
|
|
355
|
+
</w:style>
|
|
356
|
+
|
|
357
|
+
<w:style w:type="paragraph" w:styleId="Heading6">
|
|
358
|
+
<w:name w:val="heading 6"/>
|
|
359
|
+
<w:basedOn w:val="Normal"/>
|
|
360
|
+
<w:next w:val="Normal"/>
|
|
361
|
+
<w:uiPriority w:val="9"/>
|
|
362
|
+
<w:qFormat/>
|
|
363
|
+
<w:pPr>
|
|
364
|
+
<w:keepNext/>
|
|
365
|
+
<w:keepLines/>
|
|
366
|
+
<w:outlineLvl w:val="5"/>
|
|
367
|
+
<w:spacing w:before="120" w:after="60" w:line="360" w:lineRule="auto"/>
|
|
368
|
+
</w:pPr>
|
|
369
|
+
<w:rPr>
|
|
370
|
+
<w:b/>
|
|
371
|
+
<w:sz w:val="22"/>
|
|
372
|
+
<w:szCs w:val="22"/>
|
|
373
|
+
</w:rPr>
|
|
374
|
+
</w:style>
|
|
375
|
+
</w:styles>
|
|
376
|
+
`;
|
|
377
|
+
SETTINGS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
378
|
+
<w:settings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
379
|
+
<w:themeFontLang w:val="en-US" w:eastAsia="zh-CN"/>
|
|
380
|
+
<w:defaultTabStop w:val="720"/>
|
|
381
|
+
<w:compat/>
|
|
382
|
+
</w:settings>
|
|
383
|
+
`;
|
|
384
|
+
NUMBERING_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
385
|
+
<w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
386
|
+
<w:abstractNum w:abstractNumId="1">
|
|
387
|
+
<w:multiLevelType w:val="hybridMultilevel"/>
|
|
388
|
+
<w:lvl w:ilvl="0">
|
|
389
|
+
<w:start w:val="1"/>
|
|
390
|
+
<w:numFmt w:val="bullet"/>
|
|
391
|
+
<w:lvlText w:val="\u2022"/>
|
|
392
|
+
<w:lvlJc w:val="left"/>
|
|
393
|
+
<w:pPr><w:ind w:left="720" w:hanging="360"/></w:pPr>
|
|
394
|
+
<w:rPr><w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman" w:eastAsia="FangSong_GB2312" w:cs="Times New Roman"/></w:rPr>
|
|
395
|
+
</w:lvl>
|
|
396
|
+
<w:lvl w:ilvl="1">
|
|
397
|
+
<w:start w:val="1"/>
|
|
398
|
+
<w:numFmt w:val="bullet"/>
|
|
399
|
+
<w:lvlText w:val="\u25CB"/>
|
|
400
|
+
<w:lvlJc w:val="left"/>
|
|
401
|
+
<w:pPr><w:ind w:left="1440" w:hanging="360"/></w:pPr>
|
|
402
|
+
<w:rPr><w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman" w:eastAsia="FangSong_GB2312" w:cs="Times New Roman"/></w:rPr>
|
|
403
|
+
</w:lvl>
|
|
404
|
+
<w:lvl w:ilvl="2">
|
|
405
|
+
<w:start w:val="1"/>
|
|
406
|
+
<w:numFmt w:val="bullet"/>
|
|
407
|
+
<w:lvlText w:val="\u25A0"/>
|
|
408
|
+
<w:lvlJc w:val="left"/>
|
|
409
|
+
<w:pPr><w:ind w:left="2160" w:hanging="360"/></w:pPr>
|
|
410
|
+
<w:rPr><w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman" w:eastAsia="FangSong_GB2312" w:cs="Times New Roman"/></w:rPr>
|
|
411
|
+
</w:lvl>
|
|
412
|
+
</w:abstractNum>
|
|
413
|
+
<w:abstractNum w:abstractNumId="2">
|
|
414
|
+
<w:multiLevelType w:val="hybridMultilevel"/>
|
|
415
|
+
<w:lvl w:ilvl="0">
|
|
416
|
+
<w:start w:val="1"/>
|
|
417
|
+
<w:numFmt w:val="decimal"/>
|
|
418
|
+
<w:lvlText w:val="%1."/>
|
|
419
|
+
<w:lvlJc w:val="left"/>
|
|
420
|
+
<w:pPr><w:ind w:left="720" w:hanging="360"/></w:pPr>
|
|
421
|
+
<w:rPr><w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman" w:eastAsia="FangSong_GB2312" w:cs="Times New Roman"/></w:rPr>
|
|
422
|
+
</w:lvl>
|
|
423
|
+
<w:lvl w:ilvl="1">
|
|
424
|
+
<w:start w:val="1"/>
|
|
425
|
+
<w:numFmt w:val="decimal"/>
|
|
426
|
+
<w:lvlText w:val="%1.%2."/>
|
|
427
|
+
<w:lvlJc w:val="left"/>
|
|
428
|
+
<w:pPr><w:ind w:left="1440" w:hanging="360"/></w:pPr>
|
|
429
|
+
<w:rPr><w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman" w:eastAsia="FangSong_GB2312" w:cs="Times New Roman"/></w:rPr>
|
|
430
|
+
</w:lvl>
|
|
431
|
+
<w:lvl w:ilvl="2">
|
|
432
|
+
<w:start w:val="1"/>
|
|
433
|
+
<w:numFmt w:val="decimal"/>
|
|
434
|
+
<w:lvlText w:val="%1.%2.%3."/>
|
|
435
|
+
<w:lvlJc w:val="left"/>
|
|
436
|
+
<w:pPr><w:ind w:left="2160" w:hanging="360"/></w:pPr>
|
|
437
|
+
<w:rPr><w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman" w:eastAsia="FangSong_GB2312" w:cs="Times New Roman"/></w:rPr>
|
|
438
|
+
</w:lvl>
|
|
439
|
+
</w:abstractNum>
|
|
440
|
+
<w:num w:numId="1"><w:abstractNumId w:val="1"/></w:num>
|
|
441
|
+
<w:num w:numId="2"><w:abstractNumId w:val="2"/></w:num>
|
|
442
|
+
</w:numbering>
|
|
443
|
+
`;
|
|
444
|
+
HEADER1_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
445
|
+
<w:hdr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
446
|
+
<w:p>
|
|
447
|
+
<w:pPr><w:jc w:val="center"/></w:pPr>
|
|
448
|
+
<w:r><w:t></w:t></w:r>
|
|
449
|
+
</w:p>
|
|
450
|
+
</w:hdr>
|
|
451
|
+
`;
|
|
452
|
+
FOOTER1_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
453
|
+
<w:ftr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
|
|
454
|
+
<w:p>
|
|
455
|
+
<w:pPr><w:jc w:val="right"/></w:pPr>
|
|
456
|
+
<w:r><w:t>\u7B2C </w:t></w:r>
|
|
457
|
+
<w:r><w:fldChar w:fldCharType="begin"/></w:r>
|
|
458
|
+
<w:r><w:instrText xml:space="preserve"> PAGE </w:instrText></w:r>
|
|
459
|
+
<w:r><w:fldChar w:fldCharType="separate"/></w:r>
|
|
460
|
+
<w:r><w:t>1</w:t></w:r>
|
|
461
|
+
<w:r><w:fldChar w:fldCharType="end"/></w:r>
|
|
462
|
+
<w:r><w:t> \u9875 / \u5171 </w:t></w:r>
|
|
463
|
+
<w:r><w:fldChar w:fldCharType="begin"/></w:r>
|
|
464
|
+
<w:r><w:instrText xml:space="preserve"> NUMPAGES </w:instrText></w:r>
|
|
465
|
+
<w:r><w:fldChar w:fldCharType="separate"/></w:r>
|
|
466
|
+
<w:r><w:t>1</w:t></w:r>
|
|
467
|
+
<w:r><w:fldChar w:fldCharType="end"/></w:r>
|
|
468
|
+
<w:r><w:t> \u9875</w:t></w:r>
|
|
469
|
+
</w:p>
|
|
470
|
+
</w:ftr>
|
|
471
|
+
`;
|
|
472
|
+
THEME_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
473
|
+
<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme">
|
|
474
|
+
<a:themeElements>
|
|
475
|
+
<a:clrScheme name="Office">
|
|
476
|
+
<a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1>
|
|
477
|
+
<a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1>
|
|
478
|
+
<a:dk2><a:srgbClr val="1F497D"/></a:dk2>
|
|
479
|
+
<a:lt2><a:srgbClr val="EEECE1"/></a:lt2>
|
|
480
|
+
<a:accent1><a:srgbClr val="4F81BD"/></a:accent1>
|
|
481
|
+
<a:accent2><a:srgbClr val="C0504D"/></a:accent2>
|
|
482
|
+
<a:accent3><a:srgbClr val="9BBB59"/></a:accent3>
|
|
483
|
+
<a:accent4><a:srgbClr val="8064A2"/></a:accent4>
|
|
484
|
+
<a:accent5><a:srgbClr val="4BACC6"/></a:accent5>
|
|
485
|
+
<a:accent6><a:srgbClr val="F79646"/></a:accent6>
|
|
486
|
+
<a:hlink><a:srgbClr val="0000FF"/></a:hlink>
|
|
487
|
+
<a:folHlink><a:srgbClr val="800080"/></a:folHlink>
|
|
488
|
+
</a:clrScheme>
|
|
489
|
+
<a:fontScheme name="Office">
|
|
490
|
+
<a:majorFont>
|
|
491
|
+
<a:latin typeface="Times New Roman"/>
|
|
492
|
+
<a:ea typeface="FangSong_GB2312"/>
|
|
493
|
+
<a:cs typeface="Times New Roman"/>
|
|
494
|
+
</a:majorFont>
|
|
495
|
+
<a:minorFont>
|
|
496
|
+
<a:latin typeface="Times New Roman"/>
|
|
497
|
+
<a:ea typeface="FangSong_GB2312"/>
|
|
498
|
+
<a:cs typeface="Times New Roman"/>
|
|
499
|
+
</a:minorFont>
|
|
500
|
+
</a:fontScheme>
|
|
501
|
+
<a:fmtScheme name="Office">
|
|
502
|
+
<a:fillStyleLst>
|
|
503
|
+
<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
|
|
504
|
+
<a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"/></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"/></a:gs></a:gsLst><a:lin ang="16200000" scaled="1"/></a:gradFill>
|
|
505
|
+
<a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"/></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"/></a:gs></a:gsLst><a:lin ang="16200000" scaled="1"/></a:gradFill>
|
|
506
|
+
</a:fillStyleLst>
|
|
507
|
+
<a:lnStyleLst>
|
|
508
|
+
<a:ln w="9525" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln>
|
|
509
|
+
<a:ln w="25400" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln>
|
|
510
|
+
<a:ln w="38100" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln>
|
|
511
|
+
</a:lnStyleLst>
|
|
512
|
+
<a:effectStyleLst>
|
|
513
|
+
<a:effectStyle><a:effectLst/></a:effectStyle>
|
|
514
|
+
<a:effectStyle><a:effectLst/></a:effectStyle>
|
|
515
|
+
<a:effectStyle><a:effectLst/></a:effectStyle>
|
|
516
|
+
</a:effectStyleLst>
|
|
517
|
+
<a:bgFillStyleLst>
|
|
518
|
+
<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
|
|
519
|
+
<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
|
|
520
|
+
<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
|
|
521
|
+
</a:bgFillStyleLst>
|
|
522
|
+
</a:fmtScheme>
|
|
523
|
+
</a:themeElements>
|
|
524
|
+
<a:objectDefaults/>
|
|
525
|
+
<a:extraClrSchemeLst/>
|
|
526
|
+
</a:theme>
|
|
527
|
+
`;
|
|
528
|
+
FONT_TABLE_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
529
|
+
<w:fonts xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
530
|
+
<w:font w:name="Times New Roman">
|
|
531
|
+
<w:charset w:val="00"/>
|
|
532
|
+
<w:family w:val="roman"/>
|
|
533
|
+
<w:pitch w:val="variable"/>
|
|
534
|
+
</w:font>
|
|
535
|
+
<w:font w:name="FangSong_GB2312">
|
|
536
|
+
<w:charset w:val="86"/>
|
|
537
|
+
<w:family w:val="auto"/>
|
|
538
|
+
<w:pitch w:val="variable"/>
|
|
539
|
+
</w:font>
|
|
540
|
+
</w:fonts>
|
|
541
|
+
`;
|
|
194
542
|
ROOT_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
195
543
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
196
544
|
<Relationship Id="rId1"
|
|
@@ -215,6 +563,12 @@ function shouldPreserveSpace(text) {
|
|
|
215
563
|
if (!text) return false;
|
|
216
564
|
return /^\s/.test(text) || /\s$/.test(text) || /\s{2,}/.test(text);
|
|
217
565
|
}
|
|
566
|
+
function shouldKeepWhitespaceOnlyRun(text) {
|
|
567
|
+
if (!text) return false;
|
|
568
|
+
if (/\r|\n/.test(text)) return false;
|
|
569
|
+
if (text.includes("\xA0")) return true;
|
|
570
|
+
return /\s{2,}/.test(text);
|
|
571
|
+
}
|
|
218
572
|
function parseStyleAttribute(style) {
|
|
219
573
|
if (!style) return {};
|
|
220
574
|
const normalized = style.replace(/\r/g, "\n");
|
|
@@ -244,6 +598,17 @@ function parseCssColorToHex(value) {
|
|
|
244
598
|
if (hex) return hex.toUpperCase();
|
|
245
599
|
return parseRgbToHex(v);
|
|
246
600
|
}
|
|
601
|
+
function extractBackgroundFillHex(css) {
|
|
602
|
+
const raw = (css["background-color"] ?? css.background)?.trim();
|
|
603
|
+
if (!raw) return void 0;
|
|
604
|
+
const direct = parseCssColorToHex(raw);
|
|
605
|
+
if (direct) return direct;
|
|
606
|
+
const hex = raw.match(/#([0-9a-fA-F]{6})/)?.[0];
|
|
607
|
+
if (hex) return parseCssColorToHex(hex);
|
|
608
|
+
const rgb = raw.match(/rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)/)?.[0];
|
|
609
|
+
if (rgb) return parseCssColorToHex(rgb);
|
|
610
|
+
return void 0;
|
|
611
|
+
}
|
|
247
612
|
function parseFontSizeToHalfPoints(value) {
|
|
248
613
|
if (!value) return void 0;
|
|
249
614
|
const v = value.trim().toLowerCase();
|
|
@@ -434,7 +799,7 @@ function collectInlineRuns(node, inherited, out, result) {
|
|
|
434
799
|
const intrinsic = parseIntrinsicImageSizePx(parsed.contentType, parsed.data);
|
|
435
800
|
const { widthPx, heightPx } = computeImageSizePx(node, intrinsic);
|
|
436
801
|
const id = result.images.length + 1;
|
|
437
|
-
const relationshipId = `rId${id}`;
|
|
802
|
+
const relationshipId = `rId${id + IMAGE_RELATIONSHIP_ID_OFFSET}`;
|
|
438
803
|
const target = `media/image${id}.${parsed.extension}`;
|
|
439
804
|
result.images.push({
|
|
440
805
|
relationshipId,
|
|
@@ -457,7 +822,7 @@ function collectInlineRuns(node, inherited, out, result) {
|
|
|
457
822
|
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);
|
|
458
823
|
const { widthPx, heightPx } = computeImageSizePx(node, intrinsic);
|
|
459
824
|
const id = result.images.length + 1;
|
|
460
|
-
const relationshipId = `rId${id}`;
|
|
825
|
+
const relationshipId = `rId${id + IMAGE_RELATIONSHIP_ID_OFFSET}`;
|
|
461
826
|
const target = `media/image${id}.${parsed.extension}`;
|
|
462
827
|
result.images.push({
|
|
463
828
|
relationshipId,
|
|
@@ -558,9 +923,12 @@ function inferFirstFontSizeHalfPoints(node) {
|
|
|
558
923
|
}
|
|
559
924
|
return void 0;
|
|
560
925
|
}
|
|
561
|
-
function buildParagraphPrXml(node, baseFontHalfPoints, extraInd) {
|
|
926
|
+
function buildParagraphPrXml(node, baseFontHalfPoints, extraInd, pStyleId) {
|
|
562
927
|
const css = parseStyleAttribute(node.attribs?.style);
|
|
563
928
|
const parts = [];
|
|
929
|
+
if (pStyleId) parts.push(`<w:pStyle w:val="${escapeXmlText(pStyleId)}"/>`);
|
|
930
|
+
const shdHex = extractBackgroundFillHex(css);
|
|
931
|
+
if (shdHex) parts.push(`<w:shd w:val="clear" w:color="auto" w:fill="${shdHex}"/>`);
|
|
564
932
|
const align = css["text-align"]?.trim().toLowerCase();
|
|
565
933
|
const jcVal = align === "center" ? "center" : align === "right" ? "right" : align === "justify" ? "both" : void 0;
|
|
566
934
|
if (jcVal) parts.push(`<w:jc w:val="${jcVal}"/>`);
|
|
@@ -611,15 +979,16 @@ function buildParagraphPrXml(node, baseFontHalfPoints, extraInd) {
|
|
|
611
979
|
if (!parts.length) return "";
|
|
612
980
|
return `<w:pPr>${parts.join("")}</w:pPr>`;
|
|
613
981
|
}
|
|
614
|
-
function buildParagraphXmlFromContainer(node, baseStyle, extraInd, result) {
|
|
615
|
-
const
|
|
616
|
-
const
|
|
982
|
+
function buildParagraphXmlFromContainer(node, baseStyle, extraInd, pStyleId, result) {
|
|
983
|
+
const containerStyle = mergeTextStyle(baseStyle, styleFromElement(node));
|
|
984
|
+
const baseFontHalfPoints = containerStyle.fontSizeHalfPoints ?? inferFirstFontSizeHalfPoints(node) ?? 28;
|
|
985
|
+
const pPrXml = buildParagraphPrXml(node, baseFontHalfPoints, extraInd, pStyleId);
|
|
617
986
|
const runs = [];
|
|
618
987
|
const res = result ?? {
|
|
619
988
|
bodyXml: "",
|
|
620
989
|
images: []
|
|
621
990
|
};
|
|
622
|
-
for (const c of node.children ?? []) collectInlineRuns(c,
|
|
991
|
+
for (const c of node.children ?? []) collectInlineRuns(c, containerStyle, runs, res);
|
|
623
992
|
const rXml = [];
|
|
624
993
|
for (const token of runs) {
|
|
625
994
|
if (token.kind === "br") {
|
|
@@ -632,7 +1001,7 @@ function buildParagraphXmlFromContainer(node, baseStyle, extraInd, result) {
|
|
|
632
1001
|
}
|
|
633
1002
|
const text = token.text;
|
|
634
1003
|
if (!text) continue;
|
|
635
|
-
if (!text.trim()) continue;
|
|
1004
|
+
if (!text.trim() && !shouldKeepWhitespaceOnlyRun(text)) continue;
|
|
636
1005
|
rXml.push(buildRunXml(token.style, text));
|
|
637
1006
|
}
|
|
638
1007
|
if (!rXml.length) return "";
|
|
@@ -652,25 +1021,30 @@ function isExplicitPageBreak(node) {
|
|
|
652
1021
|
if (after?.includes("always") || before?.includes("always")) return true;
|
|
653
1022
|
return false;
|
|
654
1023
|
}
|
|
655
|
-
function
|
|
656
|
-
const
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const items = [];
|
|
661
|
-
const stack = [...listNode.children ?? []];
|
|
662
|
-
while (stack.length) {
|
|
663
|
-
const n = stack.shift();
|
|
664
|
-
if (n.type === "tag" && n.name?.toLowerCase() === "li") items.push(n);
|
|
665
|
-
}
|
|
1024
|
+
function buildListBlocks(listNode, ordered, level, result) {
|
|
1025
|
+
const liNodes = (listNode.children ?? []).filter(
|
|
1026
|
+
(c) => c.type === "tag" && c.name?.toLowerCase() === "li"
|
|
1027
|
+
);
|
|
1028
|
+
if (!liNodes.length) return [];
|
|
666
1029
|
const out = [];
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
1030
|
+
const numId = ordered ? 2 : 1;
|
|
1031
|
+
const ilvl = Math.max(0, Math.min(8, Math.floor(level)));
|
|
1032
|
+
const leftTwips = 720 * (ilvl + 1);
|
|
1033
|
+
const hangingTwips = 360;
|
|
1034
|
+
for (const li of liNodes) {
|
|
1035
|
+
const nestedLists = [];
|
|
1036
|
+
const baseStyle = styleFromElement(li);
|
|
671
1037
|
const runs = [];
|
|
672
|
-
|
|
673
|
-
|
|
1038
|
+
for (const c of li.children ?? []) {
|
|
1039
|
+
if (c.type === "tag") {
|
|
1040
|
+
const tag = c.name?.toLowerCase();
|
|
1041
|
+
if (tag === "ul" || tag === "ol") {
|
|
1042
|
+
nestedLists.push(c);
|
|
1043
|
+
continue;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
collectInlineRuns(c, baseStyle, runs, result);
|
|
1047
|
+
}
|
|
674
1048
|
const rXml = [];
|
|
675
1049
|
for (const token of runs) {
|
|
676
1050
|
if (token.kind === "br") {
|
|
@@ -683,18 +1057,143 @@ function buildListBlocks(listNode, ordered, result) {
|
|
|
683
1057
|
}
|
|
684
1058
|
const text = token.text;
|
|
685
1059
|
if (!text) continue;
|
|
686
|
-
if (!text.trim()) continue;
|
|
1060
|
+
if (!text.trim() && !shouldKeepWhitespaceOnlyRun(text)) continue;
|
|
687
1061
|
rXml.push(buildRunXml(token.style, text));
|
|
688
1062
|
}
|
|
689
|
-
if (
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
1063
|
+
if (rXml.length) {
|
|
1064
|
+
const baseFontHalfPoints = inferFirstFontSizeHalfPoints(li) ?? 28;
|
|
1065
|
+
const pPrXml = buildParagraphPrXml(
|
|
1066
|
+
li,
|
|
1067
|
+
baseFontHalfPoints,
|
|
1068
|
+
{ leftTwips, hangingTwips },
|
|
1069
|
+
void 0
|
|
1070
|
+
);
|
|
1071
|
+
const numPrXml = `<w:numPr><w:ilvl w:val="${ilvl}"/><w:numId w:val="${numId}"/></w:numPr>`;
|
|
1072
|
+
const mergedPPrXml = pPrXml ? pPrXml.replace("<w:pPr>", `<w:pPr>${numPrXml}`) : `<w:pPr>${numPrXml}<w:ind w:left="${leftTwips}" w:hanging="${hangingTwips}"/></w:pPr>`;
|
|
1073
|
+
out.push(`<w:p>${mergedPPrXml}${rXml.join("")}</w:p>`);
|
|
1074
|
+
}
|
|
1075
|
+
for (const nested of nestedLists) {
|
|
1076
|
+
const nestedOrdered = nested.name?.toLowerCase() === "ol";
|
|
1077
|
+
out.push(...buildListBlocks(nested, nestedOrdered, ilvl + 1, result));
|
|
1078
|
+
}
|
|
695
1079
|
}
|
|
696
1080
|
return out;
|
|
697
1081
|
}
|
|
1082
|
+
function parseCellWidthTwips(node) {
|
|
1083
|
+
const css = parseStyleAttribute(node.attribs?.style);
|
|
1084
|
+
const width = parseCssLengthToTwips(css.width, 28);
|
|
1085
|
+
if (typeof width !== "number" || width <= 0) return void 0;
|
|
1086
|
+
return width;
|
|
1087
|
+
}
|
|
1088
|
+
function estimateTextWidthTwips(text, baseFontHalfPoints) {
|
|
1089
|
+
const basePt = baseFontHalfPoints / 2;
|
|
1090
|
+
const cjkRegex = /[\u3400-\u4dbf\u4e00-\u9fff\u3000-\u303f\uff00-\uffef]/;
|
|
1091
|
+
let cjk = 0;
|
|
1092
|
+
let latin = 0;
|
|
1093
|
+
let spaces = 0;
|
|
1094
|
+
for (const ch of text) {
|
|
1095
|
+
if (ch === " " || ch === " ") {
|
|
1096
|
+
spaces++;
|
|
1097
|
+
continue;
|
|
1098
|
+
}
|
|
1099
|
+
if (cjkRegex.test(ch)) {
|
|
1100
|
+
cjk++;
|
|
1101
|
+
continue;
|
|
1102
|
+
}
|
|
1103
|
+
latin++;
|
|
1104
|
+
}
|
|
1105
|
+
const cjkTwips = Math.round(basePt * 20);
|
|
1106
|
+
const latinTwips = Math.round(basePt * 11);
|
|
1107
|
+
const spaceTwips = Math.round(basePt * 6);
|
|
1108
|
+
return cjk * cjkTwips + latin * latinTwips + spaces * spaceTwips;
|
|
1109
|
+
}
|
|
1110
|
+
function parseBorderShorthand(value, baseFontHalfPoints) {
|
|
1111
|
+
if (!value) return void 0;
|
|
1112
|
+
const raw = value.trim().toLowerCase();
|
|
1113
|
+
if (!raw) return void 0;
|
|
1114
|
+
if (raw === "none" || raw === "0") return { val: "nil", sz: 0 };
|
|
1115
|
+
const tokens = raw.split(/\s+/).filter(Boolean);
|
|
1116
|
+
if (!tokens.length) return void 0;
|
|
1117
|
+
const css = Object.fromEntries(tokens.map((t, i) => [`${i}`, t]));
|
|
1118
|
+
const widthToken = Object.values(css).find((t) => /^(?:\d+(?:\.\d+)?)(?:px|pt)?$/.test(t));
|
|
1119
|
+
const styleToken = Object.values(css).find(
|
|
1120
|
+
(t) => ["none", "solid", "dashed", "dotted", "double", "hidden"].includes(t)
|
|
1121
|
+
);
|
|
1122
|
+
const colorToken = Object.values(css).find((t) => t.startsWith("#") || t.startsWith("rgb("));
|
|
1123
|
+
const widthTwips = parseCssLengthToTwips(widthToken, baseFontHalfPoints);
|
|
1124
|
+
const sz = (() => {
|
|
1125
|
+
if (typeof widthTwips !== "number") return 4;
|
|
1126
|
+
if (widthTwips <= 0) return 0;
|
|
1127
|
+
return Math.max(2, Math.round(widthTwips * 0.4));
|
|
1128
|
+
})();
|
|
1129
|
+
const val = (() => {
|
|
1130
|
+
if (!styleToken) return "single";
|
|
1131
|
+
if (styleToken === "none" || styleToken === "hidden") return "nil";
|
|
1132
|
+
if (styleToken === "solid") return "single";
|
|
1133
|
+
if (styleToken === "dashed") return "dashed";
|
|
1134
|
+
if (styleToken === "dotted") return "dotted";
|
|
1135
|
+
if (styleToken === "double") return "double";
|
|
1136
|
+
return "single";
|
|
1137
|
+
})();
|
|
1138
|
+
const colorHex = parseCssColorToHex(colorToken);
|
|
1139
|
+
return { val, sz, colorHex };
|
|
1140
|
+
}
|
|
1141
|
+
function buildBorderTag(tag, border, fallbackColorHex) {
|
|
1142
|
+
const b = border ?? { val: "single", sz: 4, colorHex: fallbackColorHex };
|
|
1143
|
+
const color = (b.colorHex ?? fallbackColorHex).toUpperCase();
|
|
1144
|
+
return `<w:${tag} w:val="${b.val}" w:sz="${b.sz}" w:space="0" w:color="${color}"/>`;
|
|
1145
|
+
}
|
|
1146
|
+
function injectTableCellParagraphSpacing(pXml) {
|
|
1147
|
+
if (!pXml.includes("<w:p")) return pXml;
|
|
1148
|
+
if (!pXml.includes("<w:p>")) return pXml;
|
|
1149
|
+
const spacingXml = '<w:spacing w:before="0" w:after="0" w:line="360" w:lineRule="auto"/><w:wordWrap w:val="1"/>';
|
|
1150
|
+
if (pXml.includes("<w:pPr>")) {
|
|
1151
|
+
if (pXml.includes("<w:spacing ")) return pXml;
|
|
1152
|
+
return pXml.replace("<w:pPr>", `<w:pPr>${spacingXml}`);
|
|
1153
|
+
}
|
|
1154
|
+
return pXml.replace("<w:p>", `<w:p><w:pPr>${spacingXml}</w:pPr>`);
|
|
1155
|
+
}
|
|
1156
|
+
function buildTableCellBlocksXml(cell, baseStyle, result) {
|
|
1157
|
+
const children = cell.children ?? [];
|
|
1158
|
+
const hasBlocks = children.some((c) => {
|
|
1159
|
+
if (c.type !== "tag") return false;
|
|
1160
|
+
const tag = c.name?.toLowerCase();
|
|
1161
|
+
return tag === "p" || tag === "ul" || tag === "ol" || tag === "img" || tag === "canvas" || /^h[1-6]$/.test(tag ?? "");
|
|
1162
|
+
});
|
|
1163
|
+
const out = [];
|
|
1164
|
+
if (!hasBlocks) {
|
|
1165
|
+
const p = buildParagraphXmlFromContainer(cell, baseStyle, void 0, void 0, result);
|
|
1166
|
+
if (p) out.push(p);
|
|
1167
|
+
return out.length ? out.map(injectTableCellParagraphSpacing).join("") : "<w:p/>";
|
|
1168
|
+
}
|
|
1169
|
+
for (const c of children) {
|
|
1170
|
+
if (c.type === "tag") {
|
|
1171
|
+
const tag = c.name?.toLowerCase();
|
|
1172
|
+
if (tag === "p") {
|
|
1173
|
+
const p = buildParagraphXmlFromContainer(c, baseStyle, void 0, void 0, result);
|
|
1174
|
+
if (p) out.push(p);
|
|
1175
|
+
continue;
|
|
1176
|
+
}
|
|
1177
|
+
if (tag && /^h[1-6]$/.test(tag)) {
|
|
1178
|
+
const level = Number(tag.slice(1));
|
|
1179
|
+
const p = buildParagraphXmlFromContainer(c, baseStyle, void 0, `Heading${level}`, result);
|
|
1180
|
+
if (p) out.push(p);
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
if (tag === "ul" || tag === "ol") {
|
|
1184
|
+
out.push(...buildListBlocks(c, tag === "ol", 0, result));
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
if (tag === "img" || tag === "canvas") {
|
|
1188
|
+
const p = buildParagraphXmlFromSingleInlineNode(c, baseStyle, result);
|
|
1189
|
+
if (p) out.push(p);
|
|
1190
|
+
continue;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
if (!out.length) return "<w:p/>";
|
|
1195
|
+
return out.map(injectTableCellParagraphSpacing).join("");
|
|
1196
|
+
}
|
|
698
1197
|
function buildTableXml(tableNode, result) {
|
|
699
1198
|
const rows = [];
|
|
700
1199
|
const stack = [...tableNode.children ?? []];
|
|
@@ -703,25 +1202,137 @@ function buildTableXml(tableNode, result) {
|
|
|
703
1202
|
if (n.type === "tag" && n.name?.toLowerCase() === "tr") rows.push(n);
|
|
704
1203
|
if (n.children?.length) stack.unshift(...n.children);
|
|
705
1204
|
}
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
1205
|
+
const rowCells = rows.map(
|
|
1206
|
+
(tr) => (tr.children ?? []).filter((c) => c.type === "tag" && (c.name === "td" || c.name === "th"))
|
|
1207
|
+
);
|
|
1208
|
+
const colCount = Math.max(0, ...rowCells.map((cells) => cells.length));
|
|
1209
|
+
const maxTableWidthTwips = 9360;
|
|
1210
|
+
const estimatedColWidths = new Array(colCount).fill(0).map((_, i) => {
|
|
1211
|
+
let explicit;
|
|
1212
|
+
let estimated = 0;
|
|
1213
|
+
for (const cells of rowCells) {
|
|
1214
|
+
const cell = cells[i];
|
|
1215
|
+
if (!cell) continue;
|
|
1216
|
+
const w = parseCellWidthTwips(cell);
|
|
1217
|
+
if (typeof w === "number") explicit = explicit ?? w;
|
|
1218
|
+
const text = getTextContent(cell).replace(/\s+/g, " ").trim();
|
|
1219
|
+
if (!text) continue;
|
|
1220
|
+
const baseFontHalfPoints = inferFirstFontSizeHalfPoints(cell) ?? 28;
|
|
1221
|
+
const wTwips = estimateTextWidthTwips(text, baseFontHalfPoints) + 240;
|
|
1222
|
+
estimated = Math.max(estimated, wTwips);
|
|
1223
|
+
}
|
|
1224
|
+
const base = typeof explicit === "number" ? explicit : estimated || Math.round(maxTableWidthTwips / Math.max(1, colCount));
|
|
1225
|
+
return Math.max(720, Math.min(6e3, Math.round(base)));
|
|
1226
|
+
});
|
|
1227
|
+
const normalizedColWidths = (() => {
|
|
1228
|
+
const sum = estimatedColWidths.reduce((a, b) => a + b, 0);
|
|
1229
|
+
if (!sum) return estimatedColWidths;
|
|
1230
|
+
if (sum <= maxTableWidthTwips) return estimatedColWidths;
|
|
1231
|
+
const scaled = estimatedColWidths.map(
|
|
1232
|
+
(w) => Math.max(720, Math.floor(w * maxTableWidthTwips / sum))
|
|
710
1233
|
);
|
|
1234
|
+
const scaledSum = scaled.reduce((a, b) => a + b, 0);
|
|
1235
|
+
const diff = maxTableWidthTwips - scaledSum;
|
|
1236
|
+
if (diff !== 0 && scaled.length) scaled[scaled.length - 1] = Math.max(720, scaled[scaled.length - 1] + diff);
|
|
1237
|
+
return scaled;
|
|
1238
|
+
})();
|
|
1239
|
+
const tblGrid = `<w:tblGrid>${normalizedColWidths.map((w) => `<w:gridCol w:w="${w}"/>`).join("")}</w:tblGrid>`;
|
|
1240
|
+
const rowXml = [];
|
|
1241
|
+
for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {
|
|
1242
|
+
const tr = rows[rowIdx];
|
|
1243
|
+
const cells = rowCells[rowIdx] ?? [];
|
|
711
1244
|
const cellXml = [];
|
|
712
|
-
for (
|
|
1245
|
+
for (let i = 0; i < cells.length; i++) {
|
|
1246
|
+
const cell = cells[i];
|
|
713
1247
|
const isHeader = cell.name === "th";
|
|
714
1248
|
const baseStyle = isHeader ? { bold: true } : {};
|
|
715
|
-
const
|
|
716
|
-
const
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
)
|
|
1249
|
+
const paragraphs = buildTableCellBlocksXml(cell, baseStyle, result);
|
|
1250
|
+
const css = parseStyleAttribute(cell.attribs?.style);
|
|
1251
|
+
const widthTwips = parseCellWidthTwips(cell) ?? normalizedColWidths[i];
|
|
1252
|
+
const tcW = typeof widthTwips === "number" ? `<w:tcW w:w="${widthTwips}" w:type="dxa"/>` : `<w:tcW w:w="0" w:type="auto"/>`;
|
|
1253
|
+
const vAlign = (() => {
|
|
1254
|
+
const v = css["vertical-align"]?.trim().toLowerCase();
|
|
1255
|
+
if (!v) return "";
|
|
1256
|
+
if (v === "middle" || v === "center") return '<w:vAlign w:val="center"/>';
|
|
1257
|
+
if (v === "bottom") return '<w:vAlign w:val="bottom"/>';
|
|
1258
|
+
if (v === "top") return '<w:vAlign w:val="top"/>';
|
|
1259
|
+
return "";
|
|
1260
|
+
})();
|
|
1261
|
+
const shd = (() => {
|
|
1262
|
+
const hex = parseCssColorToHex(css["background-color"]);
|
|
1263
|
+
if (!hex) return "";
|
|
1264
|
+
return `<w:shd w:val="clear" w:color="auto" w:fill="${hex}"/>`;
|
|
1265
|
+
})();
|
|
1266
|
+
const noWrap = (() => {
|
|
1267
|
+
const ws = css["white-space"]?.trim().toLowerCase();
|
|
1268
|
+
if (ws?.includes("nowrap")) return "<w:noWrap/>";
|
|
1269
|
+
return "";
|
|
1270
|
+
})();
|
|
1271
|
+
const cellBorder = (() => {
|
|
1272
|
+
const bAll = parseBorderShorthand(css.border, 28);
|
|
1273
|
+
const bTop = parseBorderShorthand(css["border-top"] ?? css.border, 28);
|
|
1274
|
+
const bLeft = parseBorderShorthand(css["border-left"] ?? css.border, 28);
|
|
1275
|
+
const bBottom = parseBorderShorthand(css["border-bottom"] ?? css.border, 28);
|
|
1276
|
+
const bRight = parseBorderShorthand(css["border-right"] ?? css.border, 28);
|
|
1277
|
+
const any = bAll || css.border || css["border-top"] || css["border-left"] || css["border-bottom"] || css["border-right"];
|
|
1278
|
+
if (!any) return "";
|
|
1279
|
+
const fallback = bAll?.colorHex ?? "D9D9D9";
|
|
1280
|
+
return `<w:tcBorders>${buildBorderTag("top", bTop, fallback)}${buildBorderTag(
|
|
1281
|
+
"left",
|
|
1282
|
+
bLeft,
|
|
1283
|
+
fallback
|
|
1284
|
+
)}${buildBorderTag("bottom", bBottom, fallback)}${buildBorderTag(
|
|
1285
|
+
"right",
|
|
1286
|
+
bRight,
|
|
1287
|
+
fallback
|
|
1288
|
+
)}</w:tcBorders>`;
|
|
1289
|
+
})();
|
|
1290
|
+
cellXml.push(`<w:tc><w:tcPr>${tcW}${vAlign}${shd}${noWrap}${cellBorder}</w:tcPr>${paragraphs}</w:tc>`);
|
|
720
1291
|
}
|
|
721
1292
|
if (cellXml.length) rowXml.push(`<w:tr>${cellXml.join("")}</w:tr>`);
|
|
722
1293
|
}
|
|
723
|
-
const
|
|
724
|
-
const
|
|
1294
|
+
const tblCss = parseStyleAttribute(tableNode.attribs?.style);
|
|
1295
|
+
const tblAlign = (() => {
|
|
1296
|
+
const ml = tblCss["margin-left"]?.trim().toLowerCase();
|
|
1297
|
+
const mr = tblCss["margin-right"]?.trim().toLowerCase();
|
|
1298
|
+
const m = tblCss.margin?.trim().toLowerCase();
|
|
1299
|
+
if (ml === "auto" && mr === "auto" || (m?.includes("auto") ?? false)) return '<w:tblJc w:val="center"/>';
|
|
1300
|
+
const ta = tblCss["text-align"]?.trim().toLowerCase();
|
|
1301
|
+
if (ta === "center") return '<w:tblJc w:val="center"/>';
|
|
1302
|
+
if (ta === "right") return '<w:tblJc w:val="right"/>';
|
|
1303
|
+
return "";
|
|
1304
|
+
})();
|
|
1305
|
+
const tblBorder = (() => {
|
|
1306
|
+
const border = parseBorderShorthand(tblCss.border, 28);
|
|
1307
|
+
if (tblCss.border) {
|
|
1308
|
+
const fallback2 = border?.colorHex ?? "D9D9D9";
|
|
1309
|
+
return `<w:tblBorders>${buildBorderTag("top", border, fallback2)}${buildBorderTag(
|
|
1310
|
+
"left",
|
|
1311
|
+
border,
|
|
1312
|
+
fallback2
|
|
1313
|
+
)}${buildBorderTag("bottom", border, fallback2)}${buildBorderTag(
|
|
1314
|
+
"right",
|
|
1315
|
+
border,
|
|
1316
|
+
fallback2
|
|
1317
|
+
)}${buildBorderTag("insideH", border, fallback2)}${buildBorderTag(
|
|
1318
|
+
"insideV",
|
|
1319
|
+
border,
|
|
1320
|
+
fallback2
|
|
1321
|
+
)}</w:tblBorders>`;
|
|
1322
|
+
}
|
|
1323
|
+
const fallback = "D9D9D9";
|
|
1324
|
+
return `<w:tblBorders>${buildBorderTag("top", void 0, fallback)}${buildBorderTag(
|
|
1325
|
+
"left",
|
|
1326
|
+
void 0,
|
|
1327
|
+
fallback
|
|
1328
|
+
)}${buildBorderTag("bottom", void 0, fallback)}${buildBorderTag(
|
|
1329
|
+
"right",
|
|
1330
|
+
void 0,
|
|
1331
|
+
fallback
|
|
1332
|
+
)}${buildBorderTag("insideH", void 0, fallback)}${buildBorderTag("insideV", void 0, fallback)}</w:tblBorders>`;
|
|
1333
|
+
})();
|
|
1334
|
+
const tblW = `<w:tblW w:w="${normalizedColWidths.reduce((a, b) => a + b, 0)}" w:type="dxa"/>`;
|
|
1335
|
+
const tblPr = `<w:tblPr>${tblW}<w:tblLayout w:type="fixed"/>${tblAlign}${tblBorder}</w:tblPr>`;
|
|
725
1336
|
return `<w:tbl>${tblPr}${tblGrid}${rowXml.join("")}</w:tbl>`;
|
|
726
1337
|
}
|
|
727
1338
|
function buildParagraphXmlFromSingleInlineNode(node, baseStyle, result) {
|
|
@@ -731,7 +1342,71 @@ function buildParagraphXmlFromSingleInlineNode(node, baseStyle, result) {
|
|
|
731
1342
|
attribs: { style: "text-align: center;" },
|
|
732
1343
|
children: [node]
|
|
733
1344
|
};
|
|
734
|
-
return buildParagraphXmlFromContainer(wrapper, baseStyle, void 0, result);
|
|
1345
|
+
return buildParagraphXmlFromContainer(wrapper, baseStyle, void 0, void 0, result);
|
|
1346
|
+
}
|
|
1347
|
+
function isRecognizedBlockTag(tag) {
|
|
1348
|
+
if (!tag) return false;
|
|
1349
|
+
if (tag === "p") return true;
|
|
1350
|
+
if (tag === "table") return true;
|
|
1351
|
+
if (tag === "ul" || tag === "ol") return true;
|
|
1352
|
+
if (tag === "img" || tag === "canvas") return true;
|
|
1353
|
+
if (/^h[1-6]$/.test(tag)) return true;
|
|
1354
|
+
if (tag === "pre") return true;
|
|
1355
|
+
return false;
|
|
1356
|
+
}
|
|
1357
|
+
function collectDivBlocks(node, out, result) {
|
|
1358
|
+
const parentStyle = node.attribs?.style;
|
|
1359
|
+
const inlineBuffer = [];
|
|
1360
|
+
const flushInline = () => {
|
|
1361
|
+
const wrapper = {
|
|
1362
|
+
type: "tag",
|
|
1363
|
+
name: "p",
|
|
1364
|
+
attribs: { style: parentStyle },
|
|
1365
|
+
children: inlineBuffer.splice(0)
|
|
1366
|
+
};
|
|
1367
|
+
const pXml = buildParagraphXmlFromContainer(wrapper, {}, void 0, void 0, result);
|
|
1368
|
+
if (pXml) out.push(pXml);
|
|
1369
|
+
};
|
|
1370
|
+
const children = node.children ?? [];
|
|
1371
|
+
for (const child of children) {
|
|
1372
|
+
if (child.type === "tag") {
|
|
1373
|
+
const tag = child.name?.toLowerCase();
|
|
1374
|
+
if (isExplicitPageBreak(child)) {
|
|
1375
|
+
if (inlineBuffer.length) flushInline();
|
|
1376
|
+
out.push(PAGE_BREAK_XML);
|
|
1377
|
+
continue;
|
|
1378
|
+
}
|
|
1379
|
+
if (isRecognizedBlockTag(tag)) {
|
|
1380
|
+
if (inlineBuffer.length) flushInline();
|
|
1381
|
+
collectBodyBlocks(child, out, result);
|
|
1382
|
+
continue;
|
|
1383
|
+
}
|
|
1384
|
+
if (tag === "div") {
|
|
1385
|
+
const childHasRecognizedBlocks = (child.children ?? []).some((gc) => {
|
|
1386
|
+
if (gc.type !== "tag") return false;
|
|
1387
|
+
return isRecognizedBlockTag(gc.name?.toLowerCase());
|
|
1388
|
+
});
|
|
1389
|
+
if (childHasRecognizedBlocks) {
|
|
1390
|
+
if (inlineBuffer.length) flushInline();
|
|
1391
|
+
collectBodyBlocks(child, out, result);
|
|
1392
|
+
continue;
|
|
1393
|
+
}
|
|
1394
|
+
if (inlineBuffer.length) flushInline();
|
|
1395
|
+
const mergedStyle = [parentStyle, child.attribs?.style].filter(Boolean).join(";");
|
|
1396
|
+
const wrapper = {
|
|
1397
|
+
type: "tag",
|
|
1398
|
+
name: "p",
|
|
1399
|
+
attribs: { style: mergedStyle || void 0 },
|
|
1400
|
+
children: child.children ?? []
|
|
1401
|
+
};
|
|
1402
|
+
const pXml = buildParagraphXmlFromContainer(wrapper, {}, void 0, void 0, result);
|
|
1403
|
+
if (pXml) out.push(pXml);
|
|
1404
|
+
continue;
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
inlineBuffer.push(child);
|
|
1408
|
+
}
|
|
1409
|
+
if (inlineBuffer.length) flushInline();
|
|
735
1410
|
}
|
|
736
1411
|
function collectBodyBlocks(node, out, result) {
|
|
737
1412
|
if (isSkippableSubtree(node)) return;
|
|
@@ -742,7 +1417,7 @@ function collectBodyBlocks(node, out, result) {
|
|
|
742
1417
|
return;
|
|
743
1418
|
}
|
|
744
1419
|
if (tag === "p") {
|
|
745
|
-
const pXml = buildParagraphXmlFromContainer(node, {}, void 0, result);
|
|
1420
|
+
const pXml = buildParagraphXmlFromContainer(node, {}, void 0, void 0, result);
|
|
746
1421
|
if (pXml) out.push(pXml);
|
|
747
1422
|
return;
|
|
748
1423
|
}
|
|
@@ -753,7 +1428,7 @@ function collectBodyBlocks(node, out, result) {
|
|
|
753
1428
|
}
|
|
754
1429
|
if (tag && /^h[1-6]$/.test(tag)) {
|
|
755
1430
|
const level = Number(tag.slice(1));
|
|
756
|
-
const hXml = buildParagraphXmlFromContainer(node,
|
|
1431
|
+
const hXml = buildParagraphXmlFromContainer(node, {}, void 0, `Heading${level}`, result);
|
|
757
1432
|
if (hXml) out.push(hXml);
|
|
758
1433
|
return;
|
|
759
1434
|
}
|
|
@@ -763,7 +1438,11 @@ function collectBodyBlocks(node, out, result) {
|
|
|
763
1438
|
return;
|
|
764
1439
|
}
|
|
765
1440
|
if (tag === "ul" || tag === "ol") {
|
|
766
|
-
out.push(...buildListBlocks(node, tag === "ol", result));
|
|
1441
|
+
out.push(...buildListBlocks(node, tag === "ol", 0, result));
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
if (tag === "div") {
|
|
1445
|
+
collectDivBlocks(node, out, result);
|
|
767
1446
|
return;
|
|
768
1447
|
}
|
|
769
1448
|
}
|
|
@@ -826,11 +1505,12 @@ function htmlToWordBodyWithAssets(html) {
|
|
|
826
1505
|
}
|
|
827
1506
|
return result;
|
|
828
1507
|
}
|
|
829
|
-
var import_htmlparser2, PAGE_BREAK_XML;
|
|
1508
|
+
var import_htmlparser2, IMAGE_RELATIONSHIP_ID_OFFSET, PAGE_BREAK_XML;
|
|
830
1509
|
var init_htmlToWordBodyXml = __esm({
|
|
831
1510
|
"src/lib/htmlToWordBodyXml.ts"() {
|
|
832
1511
|
"use strict";
|
|
833
1512
|
import_htmlparser2 = require("htmlparser2");
|
|
1513
|
+
IMAGE_RELATIONSHIP_ID_OFFSET = 7;
|
|
834
1514
|
PAGE_BREAK_XML = '<w:p><w:r><w:br w:type="page"/></w:r></w:p>';
|
|
835
1515
|
}
|
|
836
1516
|
});
|