@yinyoudexing/xml2word 0.1.1 → 0.1.2
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-LY6DZSTW.js} +296 -46
- package/dist/htmlToWordBodyXml-LY6DZSTW.js.map +1 -0
- package/dist/index.cjs +651 -53
- 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");
|
|
@@ -434,7 +788,7 @@ function collectInlineRuns(node, inherited, out, result) {
|
|
|
434
788
|
const intrinsic = parseIntrinsicImageSizePx(parsed.contentType, parsed.data);
|
|
435
789
|
const { widthPx, heightPx } = computeImageSizePx(node, intrinsic);
|
|
436
790
|
const id = result.images.length + 1;
|
|
437
|
-
const relationshipId = `rId${id}`;
|
|
791
|
+
const relationshipId = `rId${id + IMAGE_RELATIONSHIP_ID_OFFSET}`;
|
|
438
792
|
const target = `media/image${id}.${parsed.extension}`;
|
|
439
793
|
result.images.push({
|
|
440
794
|
relationshipId,
|
|
@@ -457,7 +811,7 @@ function collectInlineRuns(node, inherited, out, result) {
|
|
|
457
811
|
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
812
|
const { widthPx, heightPx } = computeImageSizePx(node, intrinsic);
|
|
459
813
|
const id = result.images.length + 1;
|
|
460
|
-
const relationshipId = `rId${id}`;
|
|
814
|
+
const relationshipId = `rId${id + IMAGE_RELATIONSHIP_ID_OFFSET}`;
|
|
461
815
|
const target = `media/image${id}.${parsed.extension}`;
|
|
462
816
|
result.images.push({
|
|
463
817
|
relationshipId,
|
|
@@ -558,9 +912,10 @@ function inferFirstFontSizeHalfPoints(node) {
|
|
|
558
912
|
}
|
|
559
913
|
return void 0;
|
|
560
914
|
}
|
|
561
|
-
function buildParagraphPrXml(node, baseFontHalfPoints, extraInd) {
|
|
915
|
+
function buildParagraphPrXml(node, baseFontHalfPoints, extraInd, pStyleId) {
|
|
562
916
|
const css = parseStyleAttribute(node.attribs?.style);
|
|
563
917
|
const parts = [];
|
|
918
|
+
if (pStyleId) parts.push(`<w:pStyle w:val="${escapeXmlText(pStyleId)}"/>`);
|
|
564
919
|
const align = css["text-align"]?.trim().toLowerCase();
|
|
565
920
|
const jcVal = align === "center" ? "center" : align === "right" ? "right" : align === "justify" ? "both" : void 0;
|
|
566
921
|
if (jcVal) parts.push(`<w:jc w:val="${jcVal}"/>`);
|
|
@@ -611,9 +966,9 @@ function buildParagraphPrXml(node, baseFontHalfPoints, extraInd) {
|
|
|
611
966
|
if (!parts.length) return "";
|
|
612
967
|
return `<w:pPr>${parts.join("")}</w:pPr>`;
|
|
613
968
|
}
|
|
614
|
-
function buildParagraphXmlFromContainer(node, baseStyle, extraInd, result) {
|
|
969
|
+
function buildParagraphXmlFromContainer(node, baseStyle, extraInd, pStyleId, result) {
|
|
615
970
|
const baseFontHalfPoints = baseStyle.fontSizeHalfPoints ?? inferFirstFontSizeHalfPoints(node) ?? 28;
|
|
616
|
-
const pPrXml = buildParagraphPrXml(node, baseFontHalfPoints, extraInd);
|
|
971
|
+
const pPrXml = buildParagraphPrXml(node, baseFontHalfPoints, extraInd, pStyleId);
|
|
617
972
|
const runs = [];
|
|
618
973
|
const res = result ?? {
|
|
619
974
|
bodyXml: "",
|
|
@@ -632,7 +987,7 @@ function buildParagraphXmlFromContainer(node, baseStyle, extraInd, result) {
|
|
|
632
987
|
}
|
|
633
988
|
const text = token.text;
|
|
634
989
|
if (!text) continue;
|
|
635
|
-
if (!text.trim()) continue;
|
|
990
|
+
if (!text.trim() && !shouldKeepWhitespaceOnlyRun(text)) continue;
|
|
636
991
|
rXml.push(buildRunXml(token.style, text));
|
|
637
992
|
}
|
|
638
993
|
if (!rXml.length) return "";
|
|
@@ -652,25 +1007,30 @@ function isExplicitPageBreak(node) {
|
|
|
652
1007
|
if (after?.includes("always") || before?.includes("always")) return true;
|
|
653
1008
|
return false;
|
|
654
1009
|
}
|
|
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
|
-
}
|
|
1010
|
+
function buildListBlocks(listNode, ordered, level, result) {
|
|
1011
|
+
const liNodes = (listNode.children ?? []).filter(
|
|
1012
|
+
(c) => c.type === "tag" && c.name?.toLowerCase() === "li"
|
|
1013
|
+
);
|
|
1014
|
+
if (!liNodes.length) return [];
|
|
666
1015
|
const out = [];
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
1016
|
+
const numId = ordered ? 2 : 1;
|
|
1017
|
+
const ilvl = Math.max(0, Math.min(8, Math.floor(level)));
|
|
1018
|
+
const leftTwips = 720 * (ilvl + 1);
|
|
1019
|
+
const hangingTwips = 360;
|
|
1020
|
+
for (const li of liNodes) {
|
|
1021
|
+
const nestedLists = [];
|
|
670
1022
|
const baseStyle = {};
|
|
671
1023
|
const runs = [];
|
|
672
|
-
|
|
673
|
-
|
|
1024
|
+
for (const c of li.children ?? []) {
|
|
1025
|
+
if (c.type === "tag") {
|
|
1026
|
+
const tag = c.name?.toLowerCase();
|
|
1027
|
+
if (tag === "ul" || tag === "ol") {
|
|
1028
|
+
nestedLists.push(c);
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
collectInlineRuns(c, baseStyle, runs, result);
|
|
1033
|
+
}
|
|
674
1034
|
const rXml = [];
|
|
675
1035
|
for (const token of runs) {
|
|
676
1036
|
if (token.kind === "br") {
|
|
@@ -683,18 +1043,143 @@ function buildListBlocks(listNode, ordered, result) {
|
|
|
683
1043
|
}
|
|
684
1044
|
const text = token.text;
|
|
685
1045
|
if (!text) continue;
|
|
686
|
-
if (!text.trim()) continue;
|
|
1046
|
+
if (!text.trim() && !shouldKeepWhitespaceOnlyRun(text)) continue;
|
|
687
1047
|
rXml.push(buildRunXml(token.style, text));
|
|
688
1048
|
}
|
|
689
|
-
if (
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
1049
|
+
if (rXml.length) {
|
|
1050
|
+
const baseFontHalfPoints = inferFirstFontSizeHalfPoints(li) ?? 28;
|
|
1051
|
+
const pPrXml = buildParagraphPrXml(
|
|
1052
|
+
li,
|
|
1053
|
+
baseFontHalfPoints,
|
|
1054
|
+
{ leftTwips, hangingTwips },
|
|
1055
|
+
void 0
|
|
1056
|
+
);
|
|
1057
|
+
const numPrXml = `<w:numPr><w:ilvl w:val="${ilvl}"/><w:numId w:val="${numId}"/></w:numPr>`;
|
|
1058
|
+
const mergedPPrXml = pPrXml ? pPrXml.replace("<w:pPr>", `<w:pPr>${numPrXml}`) : `<w:pPr>${numPrXml}<w:ind w:left="${leftTwips}" w:hanging="${hangingTwips}"/></w:pPr>`;
|
|
1059
|
+
out.push(`<w:p>${mergedPPrXml}${rXml.join("")}</w:p>`);
|
|
1060
|
+
}
|
|
1061
|
+
for (const nested of nestedLists) {
|
|
1062
|
+
const nestedOrdered = nested.name?.toLowerCase() === "ol";
|
|
1063
|
+
out.push(...buildListBlocks(nested, nestedOrdered, ilvl + 1, result));
|
|
1064
|
+
}
|
|
695
1065
|
}
|
|
696
1066
|
return out;
|
|
697
1067
|
}
|
|
1068
|
+
function parseCellWidthTwips(node) {
|
|
1069
|
+
const css = parseStyleAttribute(node.attribs?.style);
|
|
1070
|
+
const width = parseCssLengthToTwips(css.width, 28);
|
|
1071
|
+
if (typeof width !== "number" || width <= 0) return void 0;
|
|
1072
|
+
return width;
|
|
1073
|
+
}
|
|
1074
|
+
function estimateTextWidthTwips(text, baseFontHalfPoints) {
|
|
1075
|
+
const basePt = baseFontHalfPoints / 2;
|
|
1076
|
+
const cjkRegex = /[\u3400-\u4dbf\u4e00-\u9fff\u3000-\u303f\uff00-\uffef]/;
|
|
1077
|
+
let cjk = 0;
|
|
1078
|
+
let latin = 0;
|
|
1079
|
+
let spaces = 0;
|
|
1080
|
+
for (const ch of text) {
|
|
1081
|
+
if (ch === " " || ch === " ") {
|
|
1082
|
+
spaces++;
|
|
1083
|
+
continue;
|
|
1084
|
+
}
|
|
1085
|
+
if (cjkRegex.test(ch)) {
|
|
1086
|
+
cjk++;
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
latin++;
|
|
1090
|
+
}
|
|
1091
|
+
const cjkTwips = Math.round(basePt * 20);
|
|
1092
|
+
const latinTwips = Math.round(basePt * 11);
|
|
1093
|
+
const spaceTwips = Math.round(basePt * 6);
|
|
1094
|
+
return cjk * cjkTwips + latin * latinTwips + spaces * spaceTwips;
|
|
1095
|
+
}
|
|
1096
|
+
function parseBorderShorthand(value, baseFontHalfPoints) {
|
|
1097
|
+
if (!value) return void 0;
|
|
1098
|
+
const raw = value.trim().toLowerCase();
|
|
1099
|
+
if (!raw) return void 0;
|
|
1100
|
+
if (raw === "none" || raw === "0") return { val: "nil", sz: 0 };
|
|
1101
|
+
const tokens = raw.split(/\s+/).filter(Boolean);
|
|
1102
|
+
if (!tokens.length) return void 0;
|
|
1103
|
+
const css = Object.fromEntries(tokens.map((t, i) => [`${i}`, t]));
|
|
1104
|
+
const widthToken = Object.values(css).find((t) => /^(?:\d+(?:\.\d+)?)(?:px|pt)?$/.test(t));
|
|
1105
|
+
const styleToken = Object.values(css).find(
|
|
1106
|
+
(t) => ["none", "solid", "dashed", "dotted", "double", "hidden"].includes(t)
|
|
1107
|
+
);
|
|
1108
|
+
const colorToken = Object.values(css).find((t) => t.startsWith("#") || t.startsWith("rgb("));
|
|
1109
|
+
const widthTwips = parseCssLengthToTwips(widthToken, baseFontHalfPoints);
|
|
1110
|
+
const sz = (() => {
|
|
1111
|
+
if (typeof widthTwips !== "number") return 4;
|
|
1112
|
+
if (widthTwips <= 0) return 0;
|
|
1113
|
+
return Math.max(2, Math.round(widthTwips * 0.4));
|
|
1114
|
+
})();
|
|
1115
|
+
const val = (() => {
|
|
1116
|
+
if (!styleToken) return "single";
|
|
1117
|
+
if (styleToken === "none" || styleToken === "hidden") return "nil";
|
|
1118
|
+
if (styleToken === "solid") return "single";
|
|
1119
|
+
if (styleToken === "dashed") return "dashed";
|
|
1120
|
+
if (styleToken === "dotted") return "dotted";
|
|
1121
|
+
if (styleToken === "double") return "double";
|
|
1122
|
+
return "single";
|
|
1123
|
+
})();
|
|
1124
|
+
const colorHex = parseCssColorToHex(colorToken);
|
|
1125
|
+
return { val, sz, colorHex };
|
|
1126
|
+
}
|
|
1127
|
+
function buildBorderTag(tag, border, fallbackColorHex) {
|
|
1128
|
+
const b = border ?? { val: "single", sz: 4, colorHex: fallbackColorHex };
|
|
1129
|
+
const color = (b.colorHex ?? fallbackColorHex).toUpperCase();
|
|
1130
|
+
return `<w:${tag} w:val="${b.val}" w:sz="${b.sz}" w:space="0" w:color="${color}"/>`;
|
|
1131
|
+
}
|
|
1132
|
+
function injectTableCellParagraphSpacing(pXml) {
|
|
1133
|
+
if (!pXml.includes("<w:p")) return pXml;
|
|
1134
|
+
if (!pXml.includes("<w:p>")) return pXml;
|
|
1135
|
+
const spacingXml = '<w:spacing w:before="0" w:after="0" w:line="360" w:lineRule="auto"/><w:wordWrap w:val="1"/>';
|
|
1136
|
+
if (pXml.includes("<w:pPr>")) {
|
|
1137
|
+
if (pXml.includes("<w:spacing ")) return pXml;
|
|
1138
|
+
return pXml.replace("<w:pPr>", `<w:pPr>${spacingXml}`);
|
|
1139
|
+
}
|
|
1140
|
+
return pXml.replace("<w:p>", `<w:p><w:pPr>${spacingXml}</w:pPr>`);
|
|
1141
|
+
}
|
|
1142
|
+
function buildTableCellBlocksXml(cell, baseStyle, result) {
|
|
1143
|
+
const children = cell.children ?? [];
|
|
1144
|
+
const hasBlocks = children.some((c) => {
|
|
1145
|
+
if (c.type !== "tag") return false;
|
|
1146
|
+
const tag = c.name?.toLowerCase();
|
|
1147
|
+
return tag === "p" || tag === "ul" || tag === "ol" || tag === "img" || tag === "canvas" || /^h[1-6]$/.test(tag ?? "");
|
|
1148
|
+
});
|
|
1149
|
+
const out = [];
|
|
1150
|
+
if (!hasBlocks) {
|
|
1151
|
+
const p = buildParagraphXmlFromContainer(cell, baseStyle, void 0, void 0, result);
|
|
1152
|
+
if (p) out.push(p);
|
|
1153
|
+
return out.length ? out.map(injectTableCellParagraphSpacing).join("") : "<w:p/>";
|
|
1154
|
+
}
|
|
1155
|
+
for (const c of children) {
|
|
1156
|
+
if (c.type === "tag") {
|
|
1157
|
+
const tag = c.name?.toLowerCase();
|
|
1158
|
+
if (tag === "p") {
|
|
1159
|
+
const p = buildParagraphXmlFromContainer(c, baseStyle, void 0, void 0, result);
|
|
1160
|
+
if (p) out.push(p);
|
|
1161
|
+
continue;
|
|
1162
|
+
}
|
|
1163
|
+
if (tag && /^h[1-6]$/.test(tag)) {
|
|
1164
|
+
const level = Number(tag.slice(1));
|
|
1165
|
+
const p = buildParagraphXmlFromContainer(c, baseStyle, void 0, `Heading${level}`, result);
|
|
1166
|
+
if (p) out.push(p);
|
|
1167
|
+
continue;
|
|
1168
|
+
}
|
|
1169
|
+
if (tag === "ul" || tag === "ol") {
|
|
1170
|
+
out.push(...buildListBlocks(c, tag === "ol", 0, result));
|
|
1171
|
+
continue;
|
|
1172
|
+
}
|
|
1173
|
+
if (tag === "img" || tag === "canvas") {
|
|
1174
|
+
const p = buildParagraphXmlFromSingleInlineNode(c, baseStyle, result);
|
|
1175
|
+
if (p) out.push(p);
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
if (!out.length) return "<w:p/>";
|
|
1181
|
+
return out.map(injectTableCellParagraphSpacing).join("");
|
|
1182
|
+
}
|
|
698
1183
|
function buildTableXml(tableNode, result) {
|
|
699
1184
|
const rows = [];
|
|
700
1185
|
const stack = [...tableNode.children ?? []];
|
|
@@ -703,25 +1188,137 @@ function buildTableXml(tableNode, result) {
|
|
|
703
1188
|
if (n.type === "tag" && n.name?.toLowerCase() === "tr") rows.push(n);
|
|
704
1189
|
if (n.children?.length) stack.unshift(...n.children);
|
|
705
1190
|
}
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
1191
|
+
const rowCells = rows.map(
|
|
1192
|
+
(tr) => (tr.children ?? []).filter((c) => c.type === "tag" && (c.name === "td" || c.name === "th"))
|
|
1193
|
+
);
|
|
1194
|
+
const colCount = Math.max(0, ...rowCells.map((cells) => cells.length));
|
|
1195
|
+
const maxTableWidthTwips = 9360;
|
|
1196
|
+
const estimatedColWidths = new Array(colCount).fill(0).map((_, i) => {
|
|
1197
|
+
let explicit;
|
|
1198
|
+
let estimated = 0;
|
|
1199
|
+
for (const cells of rowCells) {
|
|
1200
|
+
const cell = cells[i];
|
|
1201
|
+
if (!cell) continue;
|
|
1202
|
+
const w = parseCellWidthTwips(cell);
|
|
1203
|
+
if (typeof w === "number") explicit = explicit ?? w;
|
|
1204
|
+
const text = getTextContent(cell).replace(/\s+/g, " ").trim();
|
|
1205
|
+
if (!text) continue;
|
|
1206
|
+
const baseFontHalfPoints = inferFirstFontSizeHalfPoints(cell) ?? 28;
|
|
1207
|
+
const wTwips = estimateTextWidthTwips(text, baseFontHalfPoints) + 240;
|
|
1208
|
+
estimated = Math.max(estimated, wTwips);
|
|
1209
|
+
}
|
|
1210
|
+
const base = typeof explicit === "number" ? explicit : estimated || Math.round(maxTableWidthTwips / Math.max(1, colCount));
|
|
1211
|
+
return Math.max(720, Math.min(6e3, Math.round(base)));
|
|
1212
|
+
});
|
|
1213
|
+
const normalizedColWidths = (() => {
|
|
1214
|
+
const sum = estimatedColWidths.reduce((a, b) => a + b, 0);
|
|
1215
|
+
if (!sum) return estimatedColWidths;
|
|
1216
|
+
if (sum <= maxTableWidthTwips) return estimatedColWidths;
|
|
1217
|
+
const scaled = estimatedColWidths.map(
|
|
1218
|
+
(w) => Math.max(720, Math.floor(w * maxTableWidthTwips / sum))
|
|
710
1219
|
);
|
|
1220
|
+
const scaledSum = scaled.reduce((a, b) => a + b, 0);
|
|
1221
|
+
const diff = maxTableWidthTwips - scaledSum;
|
|
1222
|
+
if (diff !== 0 && scaled.length) scaled[scaled.length - 1] = Math.max(720, scaled[scaled.length - 1] + diff);
|
|
1223
|
+
return scaled;
|
|
1224
|
+
})();
|
|
1225
|
+
const tblGrid = `<w:tblGrid>${normalizedColWidths.map((w) => `<w:gridCol w:w="${w}"/>`).join("")}</w:tblGrid>`;
|
|
1226
|
+
const rowXml = [];
|
|
1227
|
+
for (let rowIdx = 0; rowIdx < rows.length; rowIdx++) {
|
|
1228
|
+
const tr = rows[rowIdx];
|
|
1229
|
+
const cells = rowCells[rowIdx] ?? [];
|
|
711
1230
|
const cellXml = [];
|
|
712
|
-
for (
|
|
1231
|
+
for (let i = 0; i < cells.length; i++) {
|
|
1232
|
+
const cell = cells[i];
|
|
713
1233
|
const isHeader = cell.name === "th";
|
|
714
1234
|
const baseStyle = isHeader ? { bold: true } : {};
|
|
715
|
-
const
|
|
716
|
-
const
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
)
|
|
1235
|
+
const paragraphs = buildTableCellBlocksXml(cell, baseStyle, result);
|
|
1236
|
+
const css = parseStyleAttribute(cell.attribs?.style);
|
|
1237
|
+
const widthTwips = parseCellWidthTwips(cell) ?? normalizedColWidths[i];
|
|
1238
|
+
const tcW = typeof widthTwips === "number" ? `<w:tcW w:w="${widthTwips}" w:type="dxa"/>` : `<w:tcW w:w="0" w:type="auto"/>`;
|
|
1239
|
+
const vAlign = (() => {
|
|
1240
|
+
const v = css["vertical-align"]?.trim().toLowerCase();
|
|
1241
|
+
if (!v) return "";
|
|
1242
|
+
if (v === "middle" || v === "center") return '<w:vAlign w:val="center"/>';
|
|
1243
|
+
if (v === "bottom") return '<w:vAlign w:val="bottom"/>';
|
|
1244
|
+
if (v === "top") return '<w:vAlign w:val="top"/>';
|
|
1245
|
+
return "";
|
|
1246
|
+
})();
|
|
1247
|
+
const shd = (() => {
|
|
1248
|
+
const hex = parseCssColorToHex(css["background-color"]);
|
|
1249
|
+
if (!hex) return "";
|
|
1250
|
+
return `<w:shd w:val="clear" w:color="auto" w:fill="${hex}"/>`;
|
|
1251
|
+
})();
|
|
1252
|
+
const noWrap = (() => {
|
|
1253
|
+
const ws = css["white-space"]?.trim().toLowerCase();
|
|
1254
|
+
if (ws?.includes("nowrap")) return "<w:noWrap/>";
|
|
1255
|
+
return "";
|
|
1256
|
+
})();
|
|
1257
|
+
const cellBorder = (() => {
|
|
1258
|
+
const bAll = parseBorderShorthand(css.border, 28);
|
|
1259
|
+
const bTop = parseBorderShorthand(css["border-top"] ?? css.border, 28);
|
|
1260
|
+
const bLeft = parseBorderShorthand(css["border-left"] ?? css.border, 28);
|
|
1261
|
+
const bBottom = parseBorderShorthand(css["border-bottom"] ?? css.border, 28);
|
|
1262
|
+
const bRight = parseBorderShorthand(css["border-right"] ?? css.border, 28);
|
|
1263
|
+
const any = bAll || css.border || css["border-top"] || css["border-left"] || css["border-bottom"] || css["border-right"];
|
|
1264
|
+
if (!any) return "";
|
|
1265
|
+
const fallback = bAll?.colorHex ?? "D9D9D9";
|
|
1266
|
+
return `<w:tcBorders>${buildBorderTag("top", bTop, fallback)}${buildBorderTag(
|
|
1267
|
+
"left",
|
|
1268
|
+
bLeft,
|
|
1269
|
+
fallback
|
|
1270
|
+
)}${buildBorderTag("bottom", bBottom, fallback)}${buildBorderTag(
|
|
1271
|
+
"right",
|
|
1272
|
+
bRight,
|
|
1273
|
+
fallback
|
|
1274
|
+
)}</w:tcBorders>`;
|
|
1275
|
+
})();
|
|
1276
|
+
cellXml.push(`<w:tc><w:tcPr>${tcW}${vAlign}${shd}${noWrap}${cellBorder}</w:tcPr>${paragraphs}</w:tc>`);
|
|
720
1277
|
}
|
|
721
1278
|
if (cellXml.length) rowXml.push(`<w:tr>${cellXml.join("")}</w:tr>`);
|
|
722
1279
|
}
|
|
723
|
-
const
|
|
724
|
-
const
|
|
1280
|
+
const tblCss = parseStyleAttribute(tableNode.attribs?.style);
|
|
1281
|
+
const tblAlign = (() => {
|
|
1282
|
+
const ml = tblCss["margin-left"]?.trim().toLowerCase();
|
|
1283
|
+
const mr = tblCss["margin-right"]?.trim().toLowerCase();
|
|
1284
|
+
const m = tblCss.margin?.trim().toLowerCase();
|
|
1285
|
+
if (ml === "auto" && mr === "auto" || (m?.includes("auto") ?? false)) return '<w:tblJc w:val="center"/>';
|
|
1286
|
+
const ta = tblCss["text-align"]?.trim().toLowerCase();
|
|
1287
|
+
if (ta === "center") return '<w:tblJc w:val="center"/>';
|
|
1288
|
+
if (ta === "right") return '<w:tblJc w:val="right"/>';
|
|
1289
|
+
return "";
|
|
1290
|
+
})();
|
|
1291
|
+
const tblBorder = (() => {
|
|
1292
|
+
const border = parseBorderShorthand(tblCss.border, 28);
|
|
1293
|
+
if (tblCss.border) {
|
|
1294
|
+
const fallback2 = border?.colorHex ?? "D9D9D9";
|
|
1295
|
+
return `<w:tblBorders>${buildBorderTag("top", border, fallback2)}${buildBorderTag(
|
|
1296
|
+
"left",
|
|
1297
|
+
border,
|
|
1298
|
+
fallback2
|
|
1299
|
+
)}${buildBorderTag("bottom", border, fallback2)}${buildBorderTag(
|
|
1300
|
+
"right",
|
|
1301
|
+
border,
|
|
1302
|
+
fallback2
|
|
1303
|
+
)}${buildBorderTag("insideH", border, fallback2)}${buildBorderTag(
|
|
1304
|
+
"insideV",
|
|
1305
|
+
border,
|
|
1306
|
+
fallback2
|
|
1307
|
+
)}</w:tblBorders>`;
|
|
1308
|
+
}
|
|
1309
|
+
const fallback = "D9D9D9";
|
|
1310
|
+
return `<w:tblBorders>${buildBorderTag("top", void 0, fallback)}${buildBorderTag(
|
|
1311
|
+
"left",
|
|
1312
|
+
void 0,
|
|
1313
|
+
fallback
|
|
1314
|
+
)}${buildBorderTag("bottom", void 0, fallback)}${buildBorderTag(
|
|
1315
|
+
"right",
|
|
1316
|
+
void 0,
|
|
1317
|
+
fallback
|
|
1318
|
+
)}${buildBorderTag("insideH", void 0, fallback)}${buildBorderTag("insideV", void 0, fallback)}</w:tblBorders>`;
|
|
1319
|
+
})();
|
|
1320
|
+
const tblW = `<w:tblW w:w="${normalizedColWidths.reduce((a, b) => a + b, 0)}" w:type="dxa"/>`;
|
|
1321
|
+
const tblPr = `<w:tblPr>${tblW}<w:tblLayout w:type="fixed"/>${tblAlign}${tblBorder}</w:tblPr>`;
|
|
725
1322
|
return `<w:tbl>${tblPr}${tblGrid}${rowXml.join("")}</w:tbl>`;
|
|
726
1323
|
}
|
|
727
1324
|
function buildParagraphXmlFromSingleInlineNode(node, baseStyle, result) {
|
|
@@ -731,7 +1328,7 @@ function buildParagraphXmlFromSingleInlineNode(node, baseStyle, result) {
|
|
|
731
1328
|
attribs: { style: "text-align: center;" },
|
|
732
1329
|
children: [node]
|
|
733
1330
|
};
|
|
734
|
-
return buildParagraphXmlFromContainer(wrapper, baseStyle, void 0, result);
|
|
1331
|
+
return buildParagraphXmlFromContainer(wrapper, baseStyle, void 0, void 0, result);
|
|
735
1332
|
}
|
|
736
1333
|
function collectBodyBlocks(node, out, result) {
|
|
737
1334
|
if (isSkippableSubtree(node)) return;
|
|
@@ -742,7 +1339,7 @@ function collectBodyBlocks(node, out, result) {
|
|
|
742
1339
|
return;
|
|
743
1340
|
}
|
|
744
1341
|
if (tag === "p") {
|
|
745
|
-
const pXml = buildParagraphXmlFromContainer(node, {}, void 0, result);
|
|
1342
|
+
const pXml = buildParagraphXmlFromContainer(node, {}, void 0, void 0, result);
|
|
746
1343
|
if (pXml) out.push(pXml);
|
|
747
1344
|
return;
|
|
748
1345
|
}
|
|
@@ -753,7 +1350,7 @@ function collectBodyBlocks(node, out, result) {
|
|
|
753
1350
|
}
|
|
754
1351
|
if (tag && /^h[1-6]$/.test(tag)) {
|
|
755
1352
|
const level = Number(tag.slice(1));
|
|
756
|
-
const hXml = buildParagraphXmlFromContainer(node,
|
|
1353
|
+
const hXml = buildParagraphXmlFromContainer(node, {}, void 0, `Heading${level}`, result);
|
|
757
1354
|
if (hXml) out.push(hXml);
|
|
758
1355
|
return;
|
|
759
1356
|
}
|
|
@@ -763,7 +1360,7 @@ function collectBodyBlocks(node, out, result) {
|
|
|
763
1360
|
return;
|
|
764
1361
|
}
|
|
765
1362
|
if (tag === "ul" || tag === "ol") {
|
|
766
|
-
out.push(...buildListBlocks(node, tag === "ol", result));
|
|
1363
|
+
out.push(...buildListBlocks(node, tag === "ol", 0, result));
|
|
767
1364
|
return;
|
|
768
1365
|
}
|
|
769
1366
|
}
|
|
@@ -826,11 +1423,12 @@ function htmlToWordBodyWithAssets(html) {
|
|
|
826
1423
|
}
|
|
827
1424
|
return result;
|
|
828
1425
|
}
|
|
829
|
-
var import_htmlparser2, PAGE_BREAK_XML;
|
|
1426
|
+
var import_htmlparser2, IMAGE_RELATIONSHIP_ID_OFFSET, PAGE_BREAK_XML;
|
|
830
1427
|
var init_htmlToWordBodyXml = __esm({
|
|
831
1428
|
"src/lib/htmlToWordBodyXml.ts"() {
|
|
832
1429
|
"use strict";
|
|
833
1430
|
import_htmlparser2 = require("htmlparser2");
|
|
1431
|
+
IMAGE_RELATIONSHIP_ID_OFFSET = 7;
|
|
834
1432
|
PAGE_BREAK_XML = '<w:p><w:r><w:br w:type="page"/></w:r></w:p>';
|
|
835
1433
|
}
|
|
836
1434
|
});
|