import path from 'path'
import process from 'process'
import get from 'lodash-es/get.js'
import isestr from 'wsemi/src/isestr.mjs'
import isp0int from 'wsemi/src/isp0int.mjs'
import str2b64 from 'wsemi/src/str2b64.mjs'
import execProcess from 'wsemi/src/execProcess.mjs'
import getPathParent from 'wsemi/src/getPathParent.mjs'
import fsIsFile from 'wsemi/src/fsIsFile.mjs'
import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
import fsCreateFolder from 'wsemi/src/fsCreateFolder.mjs'
import ensureBins from './ensureBins.mjs'
import judgeConvertOutput, { judgeConvertFile } from './judgeConvertOutput.mjs'
//fnExe, docx2html執行檔名稱(須與bin/manifest.json之name一致)
let fnExe = 'docx2html.exe'
function isWindows() {
return process.platform === 'win32'
}
/**
* 以Microsoft Word將Docx檔轉為Html檔
*
* @param {String} fpIn 輸入來源Docx檔位置字串
* @param {String} fpOut 輸入轉出Html檔位置字串
* @param {Object} [opt={}] 輸入設定物件,預設{}
* @param {Integer} [opt.execFontGrow=0] 輸入全文件放大字型次數整數,預設0
* @returns {Promise} 回傳Promise,resolve回傳成功訊息'ok',reject回傳錯誤訊息。reject值有下列型態:參數檢測失敗為說明字串;取不到執行檔為`[code] 訊息`字串(code取自ensureBins,如`[E_OFFLINE_MISSING] ...`);docx2html.exe無法執行或離開碼非0為execProcess之訊息字串(如`code[N]:\n...`);docx2html.exe執行後自報失敗(如輸入非docx、docx損毀、Word無法開啟或無法另存)為`[E_CONVERT_FAILED] 原因`,原因為exe輸出之診斷(已依系統字碼頁解碼,不回顯輸入與輸出路徑,但Word之診斷可能自行含檔名);exe報成功而fpOut未產生或為空檔(如fpOut無副檔名,Word另存為`<fpOut>.htm`)亦為`[E_CONVERT_FAILED] output file was not generated or is empty`。docx2html.exe不論成敗離開碼皆為0,故以其輸出判定成敗:須有`success`行且無`error:`行才resolve,空輸出或未知輸出亦視為失敗;失敗(reject)時fpOut之內容不可採用,本套件不刪除fpOut既有之舊檔
* @example
*
* import w from 'wsemi'
* import WDocx2html from './src/WDocx2html.mjs'
* //import WDocx2html from 'w-docx2html/src/WDocx2html.mjs'
* //import WDocx2html from 'w-docx2html'
*
* async function test() {
*
* let fpIn = `./test/docin.docx`
* let fpOut = `./test/docout.html`
* let opt = {
* execFontGrow: 1,
* }
*
* let r = await WDocx2html(fpIn, fpOut, opt)
* console.log(r)
* // => ok
*
* w.fsDeleteFile(fpOut)
*
* }
* test()
* .catch((err) => {
* console.log('catch', err)
* })
*
*/
async function WDocx2html(fpIn, fpOut, opt = {}) {
let errTemp = null
//isWindows
if (!isWindows()) {
return Promise.reject('operating system is not windows')
}
//check
if (!fsIsFile(fpIn)) {
return Promise.reject(`fpIn[${fpIn}] does not exist`)
}
//check
if (!isestr(fpOut)) {
return Promise.reject(`fpOut[${fpOut}] is not an effective string`)
}
//execFontGrow
let execFontGrow = get(opt, 'execFontGrow')
if (!isp0int(execFontGrow)) {
execFontGrow = 0
}
//轉絕對路徑
fpIn = path.resolve(fpIn)
fpOut = path.resolve(fpOut)
//fdOut
let fdOut = getPathParent(fpOut)
if (!fsIsFolder(fdOut)) {
fsCreateFolder(fdOut)
}
//prog, 經ensureBins取得docx2html.exe: 依bin/manifest.json校驗, 首次使用時下載(或自鏡像取得), 之後自快取取得, 見README〈外部執行檔〉
//本函數以字串reject(同w-docx-split、w-html2docx), 故將ensureBins帶code之Error轉為`[code] message`
let prog = ''
let errBin = null
await ensureBins({ names: fnExe })
.then((r) => {
prog = r[fnExe]
})
.catch((err) => {
errBin = `${isestr(get(err, 'code')) ? `[${err.code}] ` : ''}${isestr(get(err, 'message')) ? err.message : String(err)}`
})
if (errBin) {
return Promise.reject(errBin)
}
// console.log('prog', prog)
//inp
let inp = {
fpIn,
fpOut,
execFontGrow,
}
// console.log('inp', inp)
//input to b64
let cInput = JSON.stringify(inp)
let b64Input = str2b64(cInput)
// console.log('b64Input', b64Input)
//execProcess, docx2html.exe以ANSI字碼頁輸出(中文系統為big5), 故codeCmd給auto使失敗訊息不亂碼
let output = ''
await execProcess(prog, b64Input, { codeCmd: 'auto' })
.then((r) => {
output = r
})
.catch((err) => {
console.log('execProcess catch', err)
errTemp = err.toString()
})
//check
if (errTemp) {
return Promise.reject(errTemp)
}
//check, docx2html.exe內部失敗時只於stdout印出`error: ...`且離開碼為0, execProcess不會reject, 故須依輸出判定
let j = judgeConvertOutput(output)
if (!j.ok) {
return Promise.reject(j.msg)
}
//check, exe報成功後另驗fpOut存在且非空(例: fpOut無副檔名時Word另存為<fpOut>.htm, exe仍報success)
let jf = judgeConvertFile(fpOut)
if (!jf.ok) {
return Promise.reject(jf.msg)
}
return 'ok'
}
//ensureBins, 供部署前預先取得外部執行檔(以靜態屬性匯出, 不加具名匯出以免改變UMD主檔之形狀)
WDocx2html.ensureBins = ensureBins
export default WDocx2html