w-html2docx 1.0.28 → 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.
@@ -1,385 +1,416 @@
1
- from win32com import client as wc
2
-
3
- #使用win32com
4
- #win32com因更新問題會出現 ImportError: DLL load failed while importing win32api: 找不到指定的模組。
5
- #1. 安裝 pip install pywin32
6
- #2. 使用系統管理員權限開啟cmd
7
- #3. cd至安裝目錄 C:\ProgramData\Anaconda3\Scripts
8
- #4. 用python安裝腳本 python pywin32_postinstall.py -install
9
-
10
- #編譯
11
- #1. 安裝編譯套件 pip install pyinstaller
12
- #2. 編譯 pyinstaller -F htmlToDocx.py
13
-
14
- #win32com教學
15
- #https://zhuanlan.zhihu.com/p/67543981
16
-
17
-
18
- def getError():
19
- import sys
20
-
21
- #exc_info
22
- type, message, traceback = sys.exc_info()
23
-
24
- #es
25
- es=[]
26
- while traceback:
27
- e={
28
- 'name':traceback.tb_frame.f_code.co_name,
29
- 'filename':traceback.tb_frame.f_code.co_filename,
30
- }
31
- es.append(e)
32
- traceback = traceback.tb_next
33
-
34
- #err
35
- err={
36
- 'type':type,
37
- 'message':message,
38
- 'traceback':es,
39
- }
40
-
41
- return err
42
-
43
-
44
- def j2o(v):
45
- #json轉物件
46
- import json
47
- return json.loads(v)
48
-
49
-
50
- def o2j(v):
51
- #物件轉json
52
- import json
53
- return json.dumps(v, ensure_ascii=False)
54
-
55
-
56
- def str2b64(v):
57
- #字串轉base64字串
58
- import base64
59
- v=base64.b64encode(v.encode('utf-8'))
60
- return str(v,'utf-8')
61
-
62
-
63
- def b642str(v):
64
- #base64字串轉字串
65
- import base64
66
- return base64.b64decode(v).decode('utf-8')
67
-
68
-
69
- def readText(fn):
70
- #讀取檔案fn內文字
71
- import codecs
72
- with codecs.open(fn,'r',encoding='utf8') as f:
73
- return f.read()
74
-
75
-
76
- def writeText(fn,str):
77
- #寫出文字str至檔案fn
78
- import codecs
79
- with codecs.open(fn,'w',encoding='utf8') as f:
80
- f.write(str)
81
-
82
-
83
- def htmlToDocx(fpInSrc, fpInTemp, fpOut, opt):
84
-
85
- #Dispatch
86
- app = wc.Dispatch('Word.Application')
87
-
88
- #正式版須隱藏
89
- app.Visible = False
90
-
91
- #不詢問使用者
92
- app.DisplayAlerts = False
93
-
94
- #Open
95
- docInTemp = app.Documents.Open(fpInTemp)
96
- docInSrc = app.Documents.Open(fpInSrc)
97
-
98
- # 取得來源文件全文 Range(含格式)
99
- rngSrc = docInSrc.Range()
100
-
101
- # 取得模板文件的主內容 Range
102
- rngDst = docInTemp.Content # docInTemp.Range()
103
-
104
- # 如果你想插在文件最後,可先收合到結尾
105
- WdCollapseEnd = 0 #wdCollapseEnd, 收合到Range結尾
106
- rngDst.Collapse(WdCollapseEnd)
107
-
108
- # 直接複製格式化文字(等同貼上,但不經過剪貼簿也不靠 Selection
109
- rngDst.FormattedText = rngSrc.FormattedText
110
-
111
- # #選擇src並複製全文
112
- # docInSrc.Activate()
113
- # app.Selection.WholeStory()
114
- # app.Selection.Copy()
115
-
116
- # #選擇模板並貼上全文
117
- # docInTemp.Activate()
118
- # app.Selection.Paste()
119
- # # WdFormatOriginalFormatting = 16
120
- # # app.Selection.PasteAndFormat(WdFormatOriginalFormatting)
121
-
122
- #選擇模板調整字型
123
- try:
124
-
125
- fontFamilies=opt['fontFamilies']
126
-
127
- if isinstance(fontFamilies, (list, tuple)) and len(fontFamilies) > 0:
128
- #print(fontFamilies)
129
-
130
- rng = docInTemp.Range()
131
- for fn in fontFamilies:
132
- # print(fn)
133
- rng.Font.Name = fn
134
-
135
- except:
136
- err=getError()
137
- print(err)
138
-
139
- #選擇模板偵測各圖片寬度是否大於滿版(412), 並限制於最大值
140
- try:
141
-
142
- imgRatioWidthMax=opt['imgRatioWidthMax']
143
- imgRatioHeightMax=opt['imgRatioHeightMax']
144
-
145
- page_setup = docInTemp.PageSetup
146
- pageW = page_setup.PageWidth #A4≈595 pt
147
- pageH = page_setup.PageHeight #A4≈842 pt
148
- leftM = page_setup.LeftMargin
149
- rightM = page_setup.RightMargin
150
- topM = page_setup.TopMargin
151
- bottomM = page_setup.BottomMargin
152
-
153
- #版心最大maxW, maxH
154
- maxW = pageW - leftM - rightM
155
- maxW = maxW * imgRatioWidthMax
156
- maxH = (pageH - topM - bottomM) * 0.937043054427296 #高度須預留圖名空間
157
- maxH = maxH * imgRatioHeightMax
158
-
159
- # 使用 docInTemp.InlineShapes 直接取得整份文件所有圖片
160
- for s in docInTemp.InlineShapes:
161
- try:
162
-
163
- #w, h
164
- w = s.Width
165
- h = s.Height
166
-
167
- #scale, 計算縮放比例
168
- scaleW = maxW / w if w > maxW else 1
169
- scaleH = maxH / h if h > maxH else 1
170
- scale = min(scaleW, scaleH)
171
-
172
- if scale < 1:
173
- s.LockAspectRatio = True
174
- s.Width = w * scale #只設width, LockAspectRatio=true會讓height依比例調整
175
-
176
- except:
177
- pass
178
-
179
- except:
180
- err=getError()
181
- print(err)
182
-
183
- #設定前後行距
184
- try:
185
-
186
- for p in docInTemp.Paragraphs:
187
-
188
- try:
189
- lf = p.Range.ListFormat
190
- except:
191
- #若可能沒有ListFormat須跳過
192
- continue
193
-
194
- listType = lf.ListType # 0,1,2,3,4
195
- listStr = lf.ListString # '•', '1.', ''...
196
-
197
- # #測試顯示ListType
198
- # try:
199
- # rng = p.Range.Duplicate
200
- # rng.End = rng.End - 1 #最末是換行符號, 不能插在換行符號後面
201
- # rng.InsertAfter(f"[{listType},{listStr}]")
202
- # except:
203
- # err = getError()
204
- # print(err)
205
-
206
- WdListNoNumbering = 0 #普通段落
207
- WdListSingleLevelNumber = 1 #單層編號清單
208
- WdListMultiLevel = 2 #多層編號清單
209
- WdListBullet = 3 #項目符號清單
210
- WdListOutlineNumbering = 4 #大綱編號
211
- is_list = (listType != WdListNoNumbering) or (listStr != "")
212
-
213
- if is_list:
214
-
215
- fmt = p.Format
216
-
217
- #關閉自動段前段後距離
218
- try:
219
- fmt.SpaceBeforeAuto = False
220
- fmt.SpaceAfterAuto = False
221
- except:
222
- pass
223
-
224
- # 設定為 0.5 行
225
- fmt.LineUnitBefore = 0.5
226
- fmt.LineUnitAfter = 0.5
227
-
228
- except:
229
- err=getError()
230
- print(err)
231
-
232
- #設定本文段落(p)與清單項目(li)之水平對齊, 已為置中者(如表格標題列, 圖名)不更動, 其餘改為左右對齊(兩端對齊), 使右側邊界齊平較為美觀
233
- try:
234
-
235
- WdAlignParagraphCenter = 1 #置中對齊
236
- WdAlignParagraphJustify = 3 #左右對齊
237
- WdOutlineLevelBodyText = 10 #本文層級, 標題(h1-h6)匯入後為大綱層級1-9
238
- WdWithInTable = 12 #Information之參數, 判斷是否位於表格內
239
-
240
- for p in docInTemp.Paragraphs:
241
- try:
242
-
243
- rng = p.Range
244
-
245
- #跳過標題段落
246
- if p.OutlineLevel != WdOutlineLevelBodyText:
247
- continue
248
-
249
- #跳過表格內段落
250
- if rng.Information(WdWithInTable):
251
- continue
252
-
253
- #跳過圖片段落
254
- if rng.InlineShapes.Count > 0:
255
- continue
256
-
257
- #跳過置中段落
258
- if p.Alignment == WdAlignParagraphCenter:
259
- continue
260
-
261
- p.Alignment = WdAlignParagraphJustify
262
-
263
- except:
264
- pass
265
-
266
- except:
267
- err=getError()
268
- print(err)
269
-
270
- #移除零寬空格佔位字元(U+200B): 來源為w-md2html於換行標記空div內插入之佔位字元, 用以撐過Word匯入不被當成空段落丟棄
271
- #於此移除後該段落成為真正的空段落(乾淨段落標記, 顯示編輯標記時亦無可見字元)
272
- try:
273
-
274
- z = chr(0x200B) #U+200B 零寬空格
275
-
276
- for p in docInTemp.Paragraphs:
277
- rng = p.Range.Duplicate
278
- rng.End = rng.End - 1 #排除段落結尾之段落標記, 避免破壞段落結構
279
- t = rng.Text
280
- if z in t:
281
- rng.Text = t.replace(z, '') #移除佔位字元, 段落標記保留 => 乾淨空段落
282
-
283
- except:
284
- err=getError()
285
- print(err)
286
-
287
- #SaveAs
288
- WdSaveFormat = 16 #wdFormatDocumentDefault=16(docx) #https://learn.microsoft.com/zh-tw/office/vba/api/word.wdsaveformat
289
- docInTemp.SaveAs2(fpOut, WdSaveFormat)
290
-
291
- # Close
292
- WdDoNotSaveChanges = 0 #wdDoNotSaveChanges
293
- docInTemp.Close(WdDoNotSaveChanges)
294
- docInSrc.Close(WdDoNotSaveChanges)
295
-
296
- #Quit
297
- WdSaveOptions = 0 #wdDoNotSaveChanges=0(不儲存擱置中變更) #https://learn.microsoft.com/zh-tw/office/vba/api/word.wdsaveoptions
298
- app.Quit(WdSaveOptions)
299
-
300
-
301
- def core(b64):
302
- state=''
303
-
304
- try:
305
-
306
- #b642str
307
- s=b642str(b64)
308
-
309
- #j2o
310
- o=j2o(s)
311
-
312
- #params
313
- fpInSrc=o['fpInSrc']
314
- fpInTemp=o['fpInTemp']
315
- fpOut=o['fpOut']
316
-
317
- #opt
318
- opt={}
319
- opt['fontFamilies']=o['fontFamilies']
320
- opt['imgRatioWidthMax']=o['imgRatioWidthMax']
321
- opt['imgRatioHeightMax']=o['imgRatioHeightMax']
322
-
323
- #htmlToDocx
324
- htmlToDocx(fpInSrc, fpInTemp, fpOut, opt)
325
-
326
- state='success'
327
- except:
328
- err=getError()
329
- state='error: '+str(err["message"])
330
-
331
- return state
332
-
333
-
334
- def run():
335
- import sys
336
-
337
- #由外部程序呼叫或直接給予檔案路徑
338
- state=''
339
- argv=sys.argv
340
- #argv=['','']
341
- if len(argv)==2:
342
-
343
- #b64
344
- b64=sys.argv[1]
345
-
346
- #core
347
- state=core(b64)
348
-
349
- else:
350
- #print(sys.argv)
351
- state='error: invalid length of argv'
352
-
353
- #print & flush
354
- print(state)
355
- sys.stdout.flush()
356
-
357
-
358
- if True:
359
- #正式版
360
-
361
- #run
362
- run()
363
-
364
-
365
- if False:
366
- #產生測試輸入b64
367
-
368
- #inp
369
- inp={
370
- 'fpInSrc':'./ztmp.html',
371
- 'fpInTemp':'./tmp.docx',
372
- 'fpOut':'./ztmp.docx',
373
- 'fontFamilies': ['標楷體','Times New Roman'], #word使用, 因由左往右設定, 故得先設定標楷體再設定Times New Roman
374
- }
375
- # print(o2j(inp))
376
-
377
- #str2b64
378
- b64=str2b64(o2j(inp))
379
- print(b64)
380
-
381
- #core
382
- state=core(b64)
383
-
384
- print(state)
385
-
1
+ from win32com import client as wc
2
+ import sys
3
+
4
+ #使用win32com
5
+ #win32com因更新問題會出現 ImportError: DLL load failed while importing win32api: 找不到指定的模組。
6
+ #1. 安裝 pip install pywin32
7
+ #2. 使用系統管理員權限開啟cmd
8
+ #3. cd至安裝目錄 C:\ProgramData\Anaconda3\Scripts
9
+ #4. 用python安裝腳本 python pywin32_postinstall.py -install
10
+
11
+ #編譯
12
+ #1. 安裝編譯套件 pip install pyinstaller
13
+ #2. 編譯 pyinstaller -F htmlToDocx.py
14
+
15
+ #win32com教學
16
+ #https://zhuanlan.zhihu.com/p/67543981
17
+
18
+
19
+ def getError():
20
+ import sys
21
+
22
+ #exc_info
23
+ type, message, traceback = sys.exc_info()
24
+
25
+ #es
26
+ es=[]
27
+ while traceback:
28
+ e={
29
+ 'name':traceback.tb_frame.f_code.co_name,
30
+ 'filename':traceback.tb_frame.f_code.co_filename,
31
+ }
32
+ es.append(e)
33
+ traceback = traceback.tb_next
34
+
35
+ #err
36
+ err={
37
+ 'type':type,
38
+ 'message':message,
39
+ 'traceback':es,
40
+ }
41
+
42
+ return err
43
+
44
+
45
+ def j2o(v):
46
+ #json轉物件
47
+ import json
48
+ return json.loads(v)
49
+
50
+
51
+ def o2j(v):
52
+ #物件轉json
53
+ import json
54
+ return json.dumps(v, ensure_ascii=False)
55
+
56
+
57
+ def str2b64(v):
58
+ #字串轉base64字串
59
+ import base64
60
+ v=base64.b64encode(v.encode('utf-8'))
61
+ return str(v,'utf-8')
62
+
63
+
64
+ def b642str(v):
65
+ #base64字串轉字串
66
+ import base64
67
+ return base64.b64decode(v).decode('utf-8')
68
+
69
+
70
+ def readText(fn):
71
+ #讀取檔案fn內文字
72
+ import codecs
73
+ with codecs.open(fn,'r',encoding='utf8') as f:
74
+ return f.read()
75
+
76
+
77
+ def writeText(fn,str):
78
+ #寫出文字str至檔案fn
79
+ import codecs
80
+ with codecs.open(fn,'w',encoding='utf8') as f:
81
+ f.write(str)
82
+
83
+
84
+ def htmlToDocxCore(app, docs, fpInSrc, fpInTemp, fpOut, opt):
85
+
86
+ #Open, 開啟之文件推入docs, 供htmlToDocx於finally關閉
87
+ docInTemp = app.Documents.Open(fpInTemp)
88
+ docs.append(docInTemp)
89
+ docInSrc = app.Documents.Open(fpInSrc)
90
+ docs.append(docInSrc)
91
+
92
+ # 取得來源文件全文 Range(含格式)
93
+ rngSrc = docInSrc.Range()
94
+
95
+ # 取得模板文件的主內容 Range
96
+ rngDst = docInTemp.Content # 或 docInTemp.Range()
97
+
98
+ # 如果你想插在文件最後,可先收合到結尾
99
+ WdCollapseEnd = 0 #wdCollapseEnd, 收合到Range結尾
100
+ rngDst.Collapse(WdCollapseEnd)
101
+
102
+ # 直接複製格式化文字(等同貼上,但不經過剪貼簿也不靠 Selection)
103
+ rngDst.FormattedText = rngSrc.FormattedText
104
+
105
+ # #選擇src並複製全文
106
+ # docInSrc.Activate()
107
+ # app.Selection.WholeStory()
108
+ # app.Selection.Copy()
109
+
110
+ # #選擇模板並貼上全文
111
+ # docInTemp.Activate()
112
+ # app.Selection.Paste()
113
+ # # WdFormatOriginalFormatting = 16
114
+ # # app.Selection.PasteAndFormat(WdFormatOriginalFormatting)
115
+
116
+ #選擇模板調整字型
117
+ try:
118
+
119
+ fontFamilies=opt['fontFamilies']
120
+
121
+ if isinstance(fontFamilies, (list, tuple)) and len(fontFamilies) > 0:
122
+ #print(fontFamilies)
123
+
124
+ rng = docInTemp.Range()
125
+ for fn in fontFamilies:
126
+ # print(fn)
127
+ rng.Font.Name = fn
128
+
129
+ except:
130
+ err=getError()
131
+ print(err, file=sys.stderr)
132
+
133
+ #選擇模板偵測各圖片寬度是否大於滿版(412), 並限制於最大值
134
+ try:
135
+
136
+ imgRatioWidthMax=opt['imgRatioWidthMax']
137
+ imgRatioHeightMax=opt['imgRatioHeightMax']
138
+
139
+ page_setup = docInTemp.PageSetup
140
+ pageW = page_setup.PageWidth #A4≈595 pt
141
+ pageH = page_setup.PageHeight #A4≈842 pt
142
+ leftM = page_setup.LeftMargin
143
+ rightM = page_setup.RightMargin
144
+ topM = page_setup.TopMargin
145
+ bottomM = page_setup.BottomMargin
146
+
147
+ #版心最大maxW, maxH
148
+ maxW = pageW - leftM - rightM
149
+ maxW = maxW * imgRatioWidthMax
150
+ maxH = (pageH - topM - bottomM) * 0.937043054427296 #高度須預留圖名空間
151
+ maxH = maxH * imgRatioHeightMax
152
+
153
+ # 使用 docInTemp.InlineShapes 直接取得整份文件所有圖片
154
+ for s in docInTemp.InlineShapes:
155
+ try:
156
+
157
+ #w, h
158
+ w = s.Width
159
+ h = s.Height
160
+
161
+ #scale, 計算縮放比例
162
+ scaleW = maxW / w if w > maxW else 1
163
+ scaleH = maxH / h if h > maxH else 1
164
+ scale = min(scaleW, scaleH)
165
+
166
+ if scale < 1:
167
+ s.LockAspectRatio = True
168
+ s.Width = w * scale #只設width, LockAspectRatio=true會讓height依比例調整
169
+
170
+ except:
171
+ pass
172
+
173
+ except:
174
+ err=getError()
175
+ print(err, file=sys.stderr)
176
+
177
+ #設定前後行距
178
+ try:
179
+
180
+ for p in docInTemp.Paragraphs:
181
+
182
+ try:
183
+ lf = p.Range.ListFormat
184
+ except:
185
+ #若可能沒有ListFormat須跳過
186
+ continue
187
+
188
+ listType = lf.ListType # 0,1,2,3,4
189
+ listStr = lf.ListString # '•', '1.', ''...
190
+
191
+ # #測試顯示ListType
192
+ # try:
193
+ # rng = p.Range.Duplicate
194
+ # rng.End = rng.End - 1 #最末是換行符號, 不能插在換行符號後面
195
+ # rng.InsertAfter(f"[{listType},{listStr}]")
196
+ # except:
197
+ # err = getError()
198
+ # print(err)
199
+
200
+ WdListNoNumbering = 0 #普通段落
201
+ WdListSingleLevelNumber = 1 #單層編號清單
202
+ WdListMultiLevel = 2 #多層編號清單
203
+ WdListBullet = 3 #項目符號清單
204
+ WdListOutlineNumbering = 4 #大綱編號
205
+ is_list = (listType != WdListNoNumbering) or (listStr != "")
206
+
207
+ if is_list:
208
+
209
+ fmt = p.Format
210
+
211
+ #關閉自動段前段後距離
212
+ try:
213
+ fmt.SpaceBeforeAuto = False
214
+ fmt.SpaceAfterAuto = False
215
+ except:
216
+ pass
217
+
218
+ # 設定為 0.5 行
219
+ fmt.LineUnitBefore = 0.5
220
+ fmt.LineUnitAfter = 0.5
221
+
222
+ except:
223
+ err=getError()
224
+ print(err, file=sys.stderr)
225
+
226
+ #設定本文段落(p)與清單項目(li)之水平對齊, 已為置中者(如表格標題列, 圖名)不更動, 其餘改為左右對齊(兩端對齊), 使右側邊界齊平較為美觀
227
+ try:
228
+
229
+ WdAlignParagraphCenter = 1 #置中對齊
230
+ WdAlignParagraphJustify = 3 #左右對齊
231
+ WdOutlineLevelBodyText = 10 #本文層級, 標題(h1-h6)匯入後為大綱層級1-9
232
+ WdWithInTable = 12 #Information之參數, 判斷是否位於表格內
233
+
234
+ for p in docInTemp.Paragraphs:
235
+ try:
236
+
237
+ rng = p.Range
238
+
239
+ #跳過標題段落
240
+ if p.OutlineLevel != WdOutlineLevelBodyText:
241
+ continue
242
+
243
+ #跳過表格內段落
244
+ if rng.Information(WdWithInTable):
245
+ continue
246
+
247
+ #跳過圖片段落
248
+ if rng.InlineShapes.Count > 0:
249
+ continue
250
+
251
+ #跳過置中段落
252
+ if p.Alignment == WdAlignParagraphCenter:
253
+ continue
254
+
255
+ p.Alignment = WdAlignParagraphJustify
256
+
257
+ except:
258
+ pass
259
+
260
+ except:
261
+ err=getError()
262
+ print(err, file=sys.stderr)
263
+
264
+ #移除零寬空格佔位字元(U+200B): 來源為w-md2html於換行標記空div內插入之佔位字元, 用以撐過Word匯入不被當成空段落丟棄
265
+ #於此移除後該段落成為真正的空段落(乾淨段落標記, 顯示編輯標記時亦無可見字元)
266
+ try:
267
+
268
+ z = chr(0x200B) #U+200B 零寬空格
269
+
270
+ for p in docInTemp.Paragraphs:
271
+ rng = p.Range.Duplicate
272
+ rng.End = rng.End - 1 #排除段落結尾之段落標記, 避免破壞段落結構
273
+ t = rng.Text
274
+ if z in t:
275
+ rng.Text = t.replace(z, '') #移除佔位字元, 段落標記保留 => 乾淨空段落
276
+
277
+ except:
278
+ err=getError()
279
+ print(err, file=sys.stderr)
280
+
281
+ #SaveAs
282
+ WdSaveFormat = 16 #wdFormatDocumentDefault=16(docx) #https://learn.microsoft.com/zh-tw/office/vba/api/word.wdsaveformat
283
+ docInTemp.SaveAs2(fpOut, WdSaveFormat)
284
+
285
+
286
+ def htmlToDocx(fpInSrc, fpInTemp, fpOut, opt):
287
+
288
+ #Dispatch
289
+ app = wc.Dispatch('Word.Application')
290
+
291
+ #正式版須隱藏
292
+ app.Visible = False
293
+
294
+ #不詢問使用者
295
+ app.DisplayAlerts = False
296
+
297
+ #docs, 已開啟之文件, 由htmlToDocxCore推入
298
+ docs = []
299
+
300
+ #任一步驟例外時亦須關閉已開啟之文件並結束Word, 否則殘留之WINWORD會鎖住模板與輸出檔, 使後續轉檔失敗
301
+ try:
302
+ htmlToDocxCore(app, docs, fpInSrc, fpInTemp, fpOut, opt)
303
+ finally:
304
+
305
+ # Close
306
+ WdDoNotSaveChanges = 0 #wdDoNotSaveChanges
307
+ for doc in reversed(docs):
308
+ try:
309
+ doc.Close(WdDoNotSaveChanges)
310
+ except:
311
+ pass
312
+
313
+ #Quit
314
+ WdSaveOptions = 0 #wdDoNotSaveChanges=0(不儲存擱置中變更) #https://learn.microsoft.com/zh-tw/office/vba/api/word.wdsaveoptions
315
+ try:
316
+ app.Quit(WdSaveOptions)
317
+ except:
318
+ pass
319
+
320
+
321
+ def core(b64):
322
+ state=''
323
+
324
+ try:
325
+
326
+ #b642str
327
+ s=b642str(b64)
328
+
329
+ #j2o
330
+ o=j2o(s)
331
+
332
+ #params
333
+ fpInSrc=o['fpInSrc']
334
+ fpInTemp=o['fpInTemp']
335
+ fpOut=o['fpOut']
336
+
337
+ #opt
338
+ opt={}
339
+ opt['fontFamilies']=o['fontFamilies']
340
+ opt['imgRatioWidthMax']=o['imgRatioWidthMax']
341
+ opt['imgRatioHeightMax']=o['imgRatioHeightMax']
342
+
343
+ #htmlToDocx
344
+ htmlToDocx(fpInSrc, fpInTemp, fpOut, opt)
345
+
346
+ state='success'
347
+ except:
348
+ err=getError()
349
+ state='error: '+err['type'].__name__+': '+str(err['message'])
350
+
351
+ return state
352
+
353
+
354
+ def run():
355
+
356
+ #stdout與stderr改為utf-8: Python寫pipe時預設用系統ANSI字碼頁(中文Windows為cp950), 呼叫端須猜編碼才能讀, 且訊息含cp950無法編碼之字元時print會拋UnicodeEncodeError;
357
+ #PyInstaller打包後不吃PYTHONIOENCODING與PYTHONUTF8, 只能於此設定
358
+ for f in (sys.stdout, sys.stderr):
359
+ try:
360
+ f.reconfigure(encoding='utf-8', errors='backslashreplace')
361
+ except:
362
+ pass
363
+
364
+ #由外部程序呼叫或直接給予檔案路徑
365
+ state=''
366
+ argv=sys.argv
367
+ #argv=['','']
368
+ if len(argv)==2:
369
+
370
+ #b64
371
+ b64=sys.argv[1]
372
+
373
+ #core
374
+ state=core(b64)
375
+
376
+ else:
377
+ #print(sys.argv)
378
+ state='error: invalid length of argv'
379
+
380
+ #print & flush
381
+ print(state)
382
+ sys.stdout.flush()
383
+
384
+ #失敗時以非0離開碼結束, 使呼叫端(如wsemi之execProcess)能直接以離開碼判定成敗, 不必解析輸出
385
+ if state != 'success':
386
+ sys.exit(1)
387
+
388
+
389
+ if True:
390
+ #正式版
391
+
392
+ #run
393
+ run()
394
+
395
+
396
+ if False:
397
+ #產生測試輸入b64
398
+
399
+ #inp
400
+ inp={
401
+ 'fpInSrc':'./ztmp.html',
402
+ 'fpInTemp':'./tmp.docx',
403
+ 'fpOut':'./ztmp.docx',
404
+ 'fontFamilies': ['標楷體','Times New Roman'], #word使用, 因由左往右設定, 故得先設定標楷體再設定Times New Roman
405
+ }
406
+ # print(o2j(inp))
407
+
408
+ #str2b64
409
+ b64=str2b64(o2j(inp))
410
+ print(b64)
411
+
412
+ #core
413
+ state=core(b64)
414
+
415
+ print(state)
416
+