WTmperDocx.mjs

import fs from 'fs'
import get from 'lodash-es/get.js'
import isnum from 'wsemi/src/isnum.mjs'
import cdbl from 'wsemi/src/cdbl.mjs'
import PizZip from 'pizzip'
import Docxtemplater from 'docxtemplater'
import ImageModule from 'docxtemplater-image-module-free'
import sizeOf from 'image-size'


function silenceConsoleWarn(fn) {
    let originalWarn = console.warn
    console.warn = () => {}
    try {
        return fn()
    }
    finally {
        console.warn = originalWarn
    }
}

/**
 * Docx模板取代器
 *
 * @param {Object} kpData 輸入轉換物件,模板內取代用鍵需用中括號包住,若鍵為keyText,模板內須須取代文字給予[[keyText]],若鍵為keyImage,模板內須須取代文字須多給予%,也就是給予[[%keyImage]]
 * @param {Object} fpTmp 輸入模板檔案路徑字串
 * @param {Object} fpOut 輸入取代後輸出檔案路徑字串
 * @param {Object} [opt={}] 輸入設定物件,預設{}
 * @param {Integer} [opt.widthMaxDef=1000] 輸入預設圖最大寬度整數,單位px,預設1000
 * @param {Integer} [opt.heightMaxDef=1000] 輸入預設圖最大高度整數,單位px,預設1000
 * @param {Object} [opt.kpWidthMax={}] 輸入指定圖鍵之最大圖寬物件,各值單位px,預設{}
 * @param {Object} [opt.kpHeightMax={}] 輸入指定圖鍵之最大圖高物件,各值單位px,預設{}
 * @returns {Promise} 回傳Promise,resolve代表成功,reject代表執行失敗
 * @example
 *
 * import wtd from './src/WTmperDocx.mjs'
 *
 * let kpData = {
 *     text: 'abc測試中文',
 *     image: './test/image.png',
 * }
 * console.log('kpData', kpData)
 *
 * let fpTmp = './test/tmp.docx'
 * let fpOut = `./test/report.docx`
 * await wtd(kpData, fpTmp, fpOut)
 *
 */
let WTmpertmerx = async(kpData, fpTmp, fpOut, opt = {}) => {

    //widthMaxDef
    let widthMaxDef = get(opt, 'widthMaxDef', null)
    if (!isnum(widthMaxDef)) {
        widthMaxDef = 1000
    }

    //heightMaxDef
    let heightMaxDef = get(opt, 'heightMaxDef', null)
    if (!isnum(heightMaxDef)) {
        heightMaxDef = 1000
    }

    //buffIn, 讀入 Word 檔為二進位
    let buffIn = fs.readFileSync(fpTmp)

    //zip, 使用pizzip解壓縮
    let zip = new PizZip(buffIn)

    //imageModule, 取代圖模組, 記得模板內的key已改用中括號且key前面要添加百分比: [[%key]]
    let optIm = {
        centered: false, //是否置中
        fileType: 'tmerx',
        getImage: function (tagValue, tagName) {
            // console.log('getImage', tagValue, tagName)

            let buff = fs.readFileSync(tagValue)

            return buff

        },
        getSize: function (img, tagValue, tagName) {
            // console.log('getSize', tagValue, tagName)

            //widthMax
            let widthMax = get(opt, `kpWidthMax.${tagName}`, null)
            if (!isnum(widthMax)) {
                widthMax = widthMaxDef
            }
            widthMax = cdbl(widthMax)

            //heightMax
            let heightMax = get(opt, `kpHeightMax.${tagName}`, null)
            if (!isnum(heightMax)) {
                heightMax = heightMaxDef
            }
            heightMax = cdbl(heightMax)

            //width, height
            let dimensions = sizeOf(img)
            let { width, height } = dimensions

            //ratio
            let ratio = 1
            if (width > widthMax || height > heightMax) {
                //圖片寬高按比例縮放
                ratio = Math.min(widthMax / width, heightMax / height)
                width *= ratio
                height *= ratio
            }

            return [width, height]
        }
    }
    let imageModule = new ImageModule(optIm)

    //tmer, 建立docxtemplater實體
    let tmer = new Docxtemplater(zip, {
        modules: [imageModule], //加載imageModule
        paragraphLoop: true, //模板內迴圈將沿用段落樣式
        linebreaks: true, //文字內\n可被視為換行符號
        delimiters: { start: '[[', end: ']]' }, //預設{{key}}會無法使用, xml內被分拆儲存導致無法解析, 故改用[[key]]
    })

    //setData
    let originalWarn = console.warn
    console.warn = () => {}
    try {
        tmer.setData(kpData) //內部會顯示Deprecated method ".setData", 執行時暫時禁用console.warn
    }
    finally {
        console.warn = originalWarn
    }

    //render, 不能攔錯, 否則tmer轉二進位數據與writeFileSync無法發現render內部錯誤
    tmer.render()
    // console.log('tmer', tmer)

    //buffOut, 轉出新二進位數據
    let buffOut = tmer
        .getZip()
        .generate({ type: 'nodebuffer' })
    // console.log('buffOut', buffOut)

    //writeFileSync
    fs.writeFileSync(fpOut, buffOut)

}


export default WTmpertmerx