w-html2docx 1.0.27 → 1.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/w-html2docx.umd.js +2 -2
- package/dist/w-html2docx.umd.js.map +1 -1
- package/docs/WHtml2docx.mjs.html +204 -170
- package/docs/autoDownloadFiles.mjs.html +7 -5
- package/docs/global.html +77 -10
- package/docs/index.html +1 -1
- package/package.json +4 -4
- package/scripts/install.mjs +25 -29
- package/src/WHtml2docx.mjs +203 -169
- package/src/autoDownloadFiles.mjs +6 -4
- package/srcPython/htmlToDocx.py +416 -385
- package/srcPython//350/252/252/346/230/216.txt +32 -24
- package/test/WHtml2docx.test.mjs +51 -0
package/scripts/install.mjs
CHANGED
|
@@ -1,29 +1,25 @@
|
|
|
1
|
-
import path from 'path'
|
|
2
|
-
import { fileURLToPath } from 'url'
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
async function init() {
|
|
7
|
-
|
|
8
|
-
//check
|
|
9
|
-
let __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
10
|
-
if (!__dirname.includes('node_modules')) {
|
|
11
|
-
return //非位於node_modules, 代表套件本身
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
console.log(err)
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
//node scripts/install.mjs
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { fileURLToPath } from 'url'
|
|
3
|
+
import autoDownloadFiles from '../src/autoDownloadFiles.mjs'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async function init() {
|
|
7
|
+
|
|
8
|
+
//check
|
|
9
|
+
let __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
10
|
+
if (!__dirname.includes('node_modules')) {
|
|
11
|
+
return //非位於node_modules, 代表套件本身
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
//autoDownloadFiles, postinstall時cwd=套件自身在node_modules內的目錄, 其依序查找<cwd>/src/與<cwd>/node_modules/w-html2docx/src/, 皆無則下載至<cwd>/src/,
|
|
15
|
+
//與執行期缺檔時之自動下載為同一套邏輯, 不另行組路徑; 已存在(重新安裝)則不重複下載
|
|
16
|
+
let { fpExe } = await autoDownloadFiles()
|
|
17
|
+
console.log(`htmlToDocx.exe is ready at [${fpExe}]`)
|
|
18
|
+
|
|
19
|
+
}
|
|
20
|
+
init()
|
|
21
|
+
.catch((err) => {
|
|
22
|
+
console.log(err)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
//node scripts/install.mjs
|
package/src/WHtml2docx.mjs
CHANGED
|
@@ -1,169 +1,203 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* @param {String}
|
|
32
|
-
* @
|
|
33
|
-
* @
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* .
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
//
|
|
91
|
-
let
|
|
92
|
-
if (!ispnum(
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
//
|
|
98
|
-
let
|
|
99
|
-
if (!
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import process from 'process'
|
|
4
|
+
import get from 'lodash-es/get.js'
|
|
5
|
+
import isestr from 'wsemi/src/isestr.mjs'
|
|
6
|
+
import isearr from 'wsemi/src/isearr.mjs'
|
|
7
|
+
import ispnum from 'wsemi/src/ispnum.mjs'
|
|
8
|
+
import cdbl from 'wsemi/src/cdbl.mjs'
|
|
9
|
+
import str2b64 from 'wsemi/src/str2b64.mjs'
|
|
10
|
+
import execProcess from 'wsemi/src/execProcess.mjs'
|
|
11
|
+
import fsIsFile from 'wsemi/src/fsIsFile.mjs'
|
|
12
|
+
import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
|
|
13
|
+
import fsCreateFolder from 'wsemi/src/fsCreateFolder.mjs'
|
|
14
|
+
import autoDownloadFiles from './autoDownloadFiles.mjs'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
let fdSrv = path.resolve()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
function isWindows() {
|
|
21
|
+
return process.platform === 'win32'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Html檔轉Docx檔
|
|
27
|
+
*
|
|
28
|
+
* 轉檔由套件內之htmlToDocx.exe調用本機Microsoft Word(win32com)完成,故須於Windows且已安裝Word之機器執行。htmlToDocx.exe若不存在(安裝時npm封鎖scripts致postinstall未執行)會於轉檔當下自動下載,下載失敗則reject並帶網址與落點。
|
|
29
|
+
* 轉檔器內部失敗(Word未安裝、模板無法開啟、另存失敗等)一律reject並帶轉檔器回報之原因;resolve前另確認輸出檔已產生且非空。輸出資料夾不存在時自動建立
|
|
30
|
+
*
|
|
31
|
+
* @param {String} fpInHtml 輸入來源Html檔位置字串
|
|
32
|
+
* @param {String} fpOutDocx 輸入轉出Docx檔位置字串
|
|
33
|
+
* @param {Object} [opt={}] 輸入設定物件,預設{}
|
|
34
|
+
* @param {String} [opt.fpInTemp=''] 輸入Docx模板檔案位置字串,有給時須存在否則reject;未給時依序取模組載入當下工作路徑之`./src/tmp.docx`或`./node_modules/w-html2docx/src/tmp.docx`(套件內建模板),預設''
|
|
35
|
+
* @param {Array|String} [opt.fontFamilies=['標楷體','Times New Roman']] 輸入轉出Docx時強制更改字型陣列或字串,陣列順序為變更順序,若有要覆寫字型得放後面。預設['標楷體','Times New Roman']
|
|
36
|
+
* @param {Number} [opt.imgRatioWidthMax=1] 輸入圖片最大寬度比例,值介於0至1,預設1
|
|
37
|
+
* @param {Number} [opt.imgRatioHeightMax=1] 輸入圖片最大高度比例,值介於0至1,預設1
|
|
38
|
+
* @param {Number} [opt.timeout=null] 輸入轉檔逾時毫秒數,預設null表示不限制;逾時時強制關閉轉檔器並reject回傳逾時訊息,惟由轉檔器經COM啟動之Word不在其程序樹內,不會一併被關閉
|
|
39
|
+
* @returns {Promise} 回傳Promise,resolve回傳'ok',reject回傳錯誤訊息字串
|
|
40
|
+
* @example
|
|
41
|
+
*
|
|
42
|
+
* import w from 'wsemi'
|
|
43
|
+
* import WHtml2docx from './src/WHtml2docx.mjs'
|
|
44
|
+
* //import WHtml2docx from 'w-html2docx/src/WHtml2docx.mjs'
|
|
45
|
+
* //import WHtml2docx from 'w-html2docx'
|
|
46
|
+
*
|
|
47
|
+
* async function test() {
|
|
48
|
+
*
|
|
49
|
+
* let fpIn = `./test/ztmp.html`
|
|
50
|
+
* let fpOut = `./test/ztmp.docx`
|
|
51
|
+
* let opt = {
|
|
52
|
+
* imgRatioWidthMax: 0.5,
|
|
53
|
+
* }
|
|
54
|
+
*
|
|
55
|
+
* let r = await WHtml2docx(fpIn, fpOut, opt)
|
|
56
|
+
* console.log(r)
|
|
57
|
+
* // => ok
|
|
58
|
+
*
|
|
59
|
+
* w.fsDeleteFile(fpOut)
|
|
60
|
+
*
|
|
61
|
+
* }
|
|
62
|
+
* test()
|
|
63
|
+
* .catch((err) => {
|
|
64
|
+
* console.log('catch', err)
|
|
65
|
+
* })
|
|
66
|
+
*
|
|
67
|
+
*/
|
|
68
|
+
async function WHtml2docx(fpInHtml, fpOutDocx, opt = {}) {
|
|
69
|
+
let errTemp = null
|
|
70
|
+
|
|
71
|
+
//isWindows
|
|
72
|
+
if (!isWindows()) {
|
|
73
|
+
return Promise.reject('operating system is not windows')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//check
|
|
77
|
+
if (!fsIsFile(fpInHtml)) {
|
|
78
|
+
return Promise.reject(`fpInHtml[${fpInHtml}] does not exist`)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
//fontFamilies
|
|
82
|
+
let fontFamilies = get(opt, 'fontFamilies')
|
|
83
|
+
if (isestr(fontFamilies)) {
|
|
84
|
+
fontFamilies = [fontFamilies]
|
|
85
|
+
}
|
|
86
|
+
if (!isearr(fontFamilies)) {
|
|
87
|
+
fontFamilies = ['標楷體', 'Times New Roman']
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//imgRatioWidthMax
|
|
91
|
+
let imgRatioWidthMax = get(opt, 'imgRatioWidthMax')
|
|
92
|
+
if (!ispnum(imgRatioWidthMax)) {
|
|
93
|
+
imgRatioWidthMax = 1
|
|
94
|
+
}
|
|
95
|
+
imgRatioWidthMax = cdbl(imgRatioWidthMax)
|
|
96
|
+
|
|
97
|
+
//imgRatioHeightMax
|
|
98
|
+
let imgRatioHeightMax = get(opt, 'imgRatioHeightMax')
|
|
99
|
+
if (!ispnum(imgRatioHeightMax)) {
|
|
100
|
+
imgRatioHeightMax = 1
|
|
101
|
+
}
|
|
102
|
+
imgRatioHeightMax = cdbl(imgRatioHeightMax)
|
|
103
|
+
|
|
104
|
+
//timeout
|
|
105
|
+
let timeout = get(opt, 'timeout')
|
|
106
|
+
if (!ispnum(timeout)) {
|
|
107
|
+
timeout = null
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
//fpInTemp
|
|
111
|
+
let fpInTemp = get(opt, 'fpInTemp')
|
|
112
|
+
if (isestr(fpInTemp)) {
|
|
113
|
+
|
|
114
|
+
//check, 呼叫端有提供時須存在, 與fpInHtml之檢查對稱; 否則會由轉檔器於開啟模板時失敗, 訊息為Word之COM錯誤而看不出是路徑問題
|
|
115
|
+
if (!fsIsFile(fpInTemp)) {
|
|
116
|
+
return Promise.reject(`fpInTemp[${fpInTemp}] does not exist`)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
|
|
122
|
+
//fnTmp
|
|
123
|
+
let fnTmp = `tmp.docx`
|
|
124
|
+
|
|
125
|
+
//fpInTemp
|
|
126
|
+
if (true) {
|
|
127
|
+
let fpTmpSrc = `${fdSrv}/src/${fnTmp}`
|
|
128
|
+
let fpTmpNM = `${fdSrv}/node_modules/w-html2docx/src/${fnTmp}`
|
|
129
|
+
if (fsIsFile(fpTmpSrc)) {
|
|
130
|
+
fpInTemp = fpTmpSrc
|
|
131
|
+
}
|
|
132
|
+
else if (fsIsFile(fpTmpNM)) {
|
|
133
|
+
fpInTemp = fpTmpNM
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
return Promise.reject(`can not find folder for ${fnTmp}`)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
//轉絕對路徑
|
|
144
|
+
fpInHtml = path.resolve(fpInHtml)
|
|
145
|
+
fpInTemp = path.resolve(fpInTemp)
|
|
146
|
+
fpOutDocx = path.resolve(fpOutDocx)
|
|
147
|
+
|
|
148
|
+
//fdOutDocx, 輸出資料夾不存在時自動建立, 否則Word另存時失敗
|
|
149
|
+
let fdOutDocx = path.dirname(fpOutDocx)
|
|
150
|
+
if (!fsIsFolder(fdOutDocx)) {
|
|
151
|
+
fsCreateFolder(fdOutDocx)
|
|
152
|
+
if (!fsIsFolder(fdOutDocx)) {
|
|
153
|
+
return Promise.reject(`can not create folder[${fdOutDocx}] for fpOutDocx`)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
//prog, 自動定位htmlToDocx.exe, 無檔案(安裝時npm封鎖scripts致postinstall未執行)則自動下載
|
|
158
|
+
let { fpExe } = await autoDownloadFiles()
|
|
159
|
+
let prog = fpExe
|
|
160
|
+
// console.log('prog', prog)
|
|
161
|
+
|
|
162
|
+
//inp
|
|
163
|
+
let inp = {
|
|
164
|
+
fpInSrc: fpInHtml,
|
|
165
|
+
fpInTemp,
|
|
166
|
+
fpOut: fpOutDocx,
|
|
167
|
+
fontFamilies,
|
|
168
|
+
imgRatioWidthMax,
|
|
169
|
+
imgRatioHeightMax,
|
|
170
|
+
}
|
|
171
|
+
// console.log('inp', inp)
|
|
172
|
+
|
|
173
|
+
//input to b64
|
|
174
|
+
let cInput = JSON.stringify(inp)
|
|
175
|
+
let b64Input = str2b64(cInput)
|
|
176
|
+
// console.log('b64Input', b64Input)
|
|
177
|
+
|
|
178
|
+
//execProcess, 轉檔器成功時印success且離開碼0, 失敗時印error: 原因且離開碼1, 故離開碼非0即reject並帶其輸出
|
|
179
|
+
// codeCmd給auto: 現行轉檔器以utf-8輸出, 尚未重新下載之舊版轉檔器以系統字碼頁(中文Windows為cp950)輸出, auto先以utf-8嚴格解碼, 遇非utf-8位元組改用系統字碼頁, 兩者皆可正確解碼; 轉檔器損毀時bootloader之ANSI訊息亦同
|
|
180
|
+
let output = ''
|
|
181
|
+
await execProcess(prog, b64Input, { codeCmd: 'auto', timeout })
|
|
182
|
+
.then((r) => {
|
|
183
|
+
output = r
|
|
184
|
+
})
|
|
185
|
+
.catch((err) => {
|
|
186
|
+
errTemp = String(err).trim()
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
//check
|
|
190
|
+
if (errTemp) {
|
|
191
|
+
return Promise.reject(errTemp)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
//check, 轉檔器回報成功仍須確認輸出檔存在且非空: 舊版轉檔器於Word未安裝、模板無法開啟等失敗時仍以離開碼0結束, 只在輸出印出error
|
|
195
|
+
if (!fsIsFile(fpOutDocx) || fs.statSync(fpOutDocx).size === 0) {
|
|
196
|
+
return Promise.reject(`fpOutDocx[${fpOutDocx}] was not generated: ${output.trim()}`)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return 'ok'
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
export default WHtml2docx
|
|
@@ -18,6 +18,9 @@ let pmDownload = null
|
|
|
18
18
|
* 依序偵測當前工作路徑的src/與node_modules/w-html2docx/src/,皆無htmlToDocx.exe時,
|
|
19
19
|
* 代表安裝時npm封鎖scripts致postinstall未執行,故自動調用downloadFiles重新下載
|
|
20
20
|
*
|
|
21
|
+
* 下載係先寫入暫存檔`htmlToDocx.exe.download`,完成後才改名為htmlToDocx.exe,故下載中途斷線或程序被終止時不會留下截斷之htmlToDocx.exe,下次呼叫會重新下載;
|
|
22
|
+
* 下載失敗(斷網、代理不可達、非2xx回應、中途斷線)時reject字串`failed to download url[網址] to file[落點]: 原因`
|
|
23
|
+
*
|
|
21
24
|
* 供w-html2docx自身與其他依賴w-html2docx的套件調用,無須各自實作偵測與下載邏輯
|
|
22
25
|
*
|
|
23
26
|
* 因htmlToDocx.exe只能用於Windows作業系統,故調用前須自行檢核作業系統
|
|
@@ -66,12 +69,11 @@ async function autoDownloadFiles() {
|
|
|
66
69
|
//併發呼叫共用同一個下載Promise, 避免重複下載
|
|
67
70
|
if (pmDownload === null) {
|
|
68
71
|
pmDownload = downloadFiles(fdBaseDL)
|
|
69
|
-
.
|
|
72
|
+
.finally(() => {
|
|
70
73
|
|
|
71
|
-
|
|
74
|
+
//下載結束(成敗皆然)歸零, 使下次呼叫重新依檔案是否存在判定; 若只於失敗時歸零, 下載成功後檔案被移除(重裝套件、防毒隔離)或程序切換工作路徑時, 會沿用已resolve之Promise而不再下載
|
|
72
75
|
pmDownload = null
|
|
73
76
|
|
|
74
|
-
return Promise.reject(err)
|
|
75
77
|
})
|
|
76
78
|
}
|
|
77
79
|
await pmDownload
|
|
@@ -85,7 +87,7 @@ async function autoDownloadFiles() {
|
|
|
85
87
|
|
|
86
88
|
//check
|
|
87
89
|
if (fdBase === '') {
|
|
88
|
-
return Promise.reject(
|
|
90
|
+
return Promise.reject(`can not find ${fnExe} in [${fdBaseSelf}] or [${fdBaseNM}]`)
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
//fpExe
|