无星的前端之旅(三十)——怎么写word

209 阅读2分钟

背景

有个业务要求,需要先进行一定的自动化操作

将提取的信息写到Word里

docxtemplater

docxtemplater-官网

docxtemplater-GitHub

集成

pnpm add docxtemplater pizzip fs-extra

如果需要插入图片

pnpm add docxtemplater pizzip fs-extra docxtemplater-image-module-free image-size

渲染

// docUtils.js
// PizZip is required because docx/pptx/xlsx files are all zipped files, and
// the PizZip library allows us to load the file in memory
const PizZip = require("pizzip");
const Docxtemplater = require("docxtemplater");
const fs = require("fs-extra");
const ImageModule = require('docxtemplater-image-module-free');
const sizeOf = require("image-size");

// 替换模板key
/**
 * filePath: 文档路径
 * renderContent: 文档中的key要替换的内容,例如{ image: "examples/image.png" , aa: "123"}
 */
const replaceTemplateKey = (filePath, renderContent) => {
    // Load the docx file as binary content
    const content = fs.readFileSync(filePath, "binary")

    const opts = {
        centered: false,
        fileType: "docx",//Or pptx
        getImage(tagValue, tagName, meta) {
            //tagValue is 'examples/image.png'
            //tagName is 'image'
            // console.log({ tagValue, tagName, meta });
            return fs.readFileSync(tagValue);
        },
        getSize(img, tagValue, tagName) {
            //Pass the function that return image size
            //img is the image returned by opts.getImage()
            //tagValue is 'examples/image.png'
            //tagName is 'image'
            //tip: you can use node module 'image-size' here
            const sizeObj = sizeOf(img);
            // console.log(sizeObj);
            const multiple = sizeObj.width / 500
            const width = sizeObj.width / multiple
            const height = sizeObj.height / multiple
            
            return [width, height];
        },
    }

    const zip = new PizZip(content);

    const doc = new Docxtemplater(zip, {
        modules: [new ImageModule(opts)],
        paragraphLoop: true,
        linebreaks: true,
    });
    // 没给的值直接给空
    // doc.setOptions({
    //     nullGetter: function () {
    //         return "";
    //     }
    // }); 
    
    doc.render(renderContent);

    const buffer = doc.getZip().generate({
        type: "nodebuffer",
        // compression: DEFLATE adds a compression step.
        // For a 50MB output document, expect 500ms additional CPU time
        compression: "DEFLATE",
    });

    fs.writeFileSync(filePath, buffer);
}
module.exports = {
    replaceTemplateKey,
}

doc模板

普通的文字

// 模板.docx

你好,我是{name}

renderContent 为

{
  name:"张三"
}

输出

你好,我是张三

图片

// 模板.docx

{%template_pic_basic_info}

renderContent 为

{
  template_pic_basic_info:"file://a/v/b/c.png"
}

输入一张图片,这里不截图了

布尔,是否渲染

// 模板.docx
{#show1}
你好,我是{name1}
{/show1}
你好,我是{name2}

renderContent 为

{
  show1:true,
  name1:"张三",
  name2:"李四"
}

输出

你好,我是张三
你好,我是李四

renderContent 为

{
  show1:false,
  name1:"张三",
  name2:"李四"
}

输出

你好,我是李四

循环

有多张图片要展示,不知道具体多少张

// 模板.docx
{#template_pic_account_history_info3}
{%image}
{/template_pic_account_history_info3}

renderContent 为

{
  template_pic_account_history_info3:[
    {image:"xxx/xxx.png"},
    {image:"xxx/xxx.png"},
    {image:"xxx/xxx.png"},
    {image:"xxx/xxx.png"},
  ],
}

渲染多张图片

End

基本上就能满足大部分的需求了