JS uses docxtemplater to export word (with pictures)

One, docxtemplater

docxtemplater is a mail merge tool that is used programmatically and handles conditions, loops, and can be extended to insert anything (tables, html, images).

  • npm is the easiest way to install docxtemplater
?
npm install docxtemplater pizzip --save

?
// install docxtemplater
npm install docxtemplater pizzip --save
// install jszip-utils
npm install jszip-utils --save
// install jszip
npm install jszip --save
// Install FileSaver
npm install file-saver --save
// Introduce plugin 1 for processing images
npm install docxtemplater-image-module-free --save
// Introduce plug-in 2 for processing pictures
npm install angular-expressions --save

Second, before exporting word, you need to prepare a word template file (according to the style you want to export), and put it in the public folder, as shown below

(here I feel that the grammar used is relatively complete, and there are other grammars that can be exchanged)

Note:

The parts that need to be filled in are defined as variables or json object arrays, the specific format is as follows:

1. Use { } to include a single variable, for example:

{name}, {user}

2. For json array format, wrap a loop object, for example:

The original format is:

list:[
 {name:'lipipi'},
 {name:'heyaya'}
]

Represented in the template file as:

{#list}{name}{/}

If the object is an image address, you need to add % before the object, for example:

Represented in the template file as:

{#imglist}
{%imgUrl}
{/imglist}

Stamping the pit: I kept reporting the error ‘%imgUrl’ in the picture, and finally found that it must be written in a new line, while other arrays can be written in one line.

3. Package JS

This part is mainly to implement the main implementation method of exporting word documents containing pictures, including converting the url path of pictures to base64 path, converting base64 to binary, and exporting pictures. You can directly copy and paste them into the page for use. The specific code is as follows:

/**
 * Export word document (with pictures)
 *
 */
import Docxtemplater from 'docxtemplater'
import PizZip from 'pizzip'
import JSZipUtils from 'jszip-utils'
import { saveAs } from 'file-saver'

/**
 * Convert data in base64 format to ArrayBuffer
 * @param {Object} dataURL data in base64 format
 */
function base64DataURLToArrayBuffer(dataURL) {
    const base64Regex = /^data:image\/(png|jpg|jpeg|svg|svg\ + xml);base64,/;
    if (!base64Regex.test(dataURL)) {
        return false;
    }
    const stringBase64 = dataURL.replace(base64Regex, "");
    let binaryString;
    if (typeof window !== "undefined") {
        binaryString = window.atob(stringBase64);
    } else {
        binaryString = Buffer.from(stringBase64, "base64").toString("binary");
    }
    const len = binaryString. length;
    const bytes = new Uint8Array(len);
    for (let i = 0; i < len; i ++ ) {
        const ascii = binaryString. charCodeAt(i);
        bytes[i] = ascii;
    }
    return bytes.buffer;
}



export const ExportBriefDataDocx = (tempDocxPath, data, fileName, imgSize) => {
    console.log(111, tempDocxPath, data, fileName, imgSize)
    //Here to introduce the plug-in for processing pictures
    var ImageModule = require('docxtemplater-image-module-free');
    var expressions = require('angular-expressions')
    var assign = require('lodash/assign')
    var last = require("lodash/last")
    expressions.filters.lower = function (input) {
        // This condition should be used to make sure that if your input is
        // undefined, your output will be undefined as well and will not
        // throw an error
        if (!input) return input
        // The toLowerCase() method is used to convert the string to lowercase.
        return input.toLowerCase()
    }
    function angularParser(tag) {
        tag = tag
            .replace(/^\.$/, 'this')
            .replace(/('|')/g, "'")
            .replace(/("|")/g, '"')
        const expr = expressions.compile(tag)
        return {
            get: function (scope, context) {
                let obj = {}
                const index = last(context.scopePathItem)
                const scopeList = context. scopeList
                const num = context.num
                for (let i = 0, len = num + 1; i < len; i ++ ) {
                    obj = assign(obj, scopeList[i])
                }
                //Use $index + 1 in the word template to create an incremental serial number
                obj = assign(obj, { $index: index })
                return expr(scope, obj)
            }
        }
    }
    JSZipUtils.getBinaryContent(tempDocxPath, (error, content) => {
        if (error) {
            console. log(error)
        }
        expressions.filters.size = function (input, width, height) {
            return {
                data: input,
                size: [width, height],
            };
        };
       
        let opts = {}

        opts = {
            //Is the image centered?
            centered: true
        };
        opts.getImage = (chartId) => {
            //Convert base64 data to ArrayBuffer
            return base64DataURLToArrayBuffer(chartId);
        }
        opts. getSize = function (img, tagValue, tagName) {
            // Customize the specified image size
            if (imgSize. hasOwnProperty(tagName)) {
                return imgSize[tagName];
            } else {
                return [200, 200];
            }
        }




        // Create a JSZip instance with the content of the template
        const zip = new PizZip(content)
        // Create and load Docxtemplater instance object
        
        // Set the value of the template variable
        
let doc = new Docxtemplater();
        doc. attachModule(new ImageModule(opts));
        doc. loadZip(zip);
        doc.setOptions({parser:angularParser});
        doc. setData(data)
        try {
            // Rendering the document will replace all internal variables with values,
            doc. render()
        } catch (error) {
            const e = {
                message: error. message,
                name: error.name,
                stack: error. stack,
                properties: error.properties

            }
            console.log('err',{ error: e })
            // When using json records, an error message is thrown here
            throw error
        }
        // Generate a zip file representing the Docxtemplater object (not a real file, but an in-memory representation)
        const out = doc.getZip().generate({
            type: 'blob',
            mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        })
        // Save the target file object as a file of the target type and name it
        saveAs(out, fileName)
    })
}

/**
 * Convert the url path of the picture to the base64 path
 * You can use await to wait for the asynchronous return of Promise
 * @param {Object} imgUrl image path
 */
export function getBase64Sync(imgUrl) {
    return new Promise(function (resolve, reject) {
        // Must be set to let, otherwise the picture will not be displayed
        let image = new Image();
        //The map's address
        image.src = imgUrl;
        // Solve cross-domain problems
        image.setAttribute("crossOrigin", '*'); // support cross-origin images
        // image.onload is asynchronous loading
        image.onload = function () {
            let canvas = document. createElement("canvas");
            canvas.width = image.width;
            canvas.height = image.height;
            let context = canvas. getContext("2d");
            context. drawImage(image, 0, 0, image. width, image. height);
            //Picture suffix name
            let ext = image.src.substring(image.src.lastIndexOf(".") + 1).toLowerCase();
            // image quality
            let quality = 0.8;
            //Convert to base64
            let dataurl = canvas.toDataURL("image/" + ext, quality);
            //return
            resolve(dataurl);
        };
    })
}

Fourth, call the export method

<script>
import {exportWord,getBase64Sync} from '@/assets/js/outword.js'
export default {
  data () {
      return {
        name:'lipipi',
        listname:'Exported Templates'
imglist:[
{
imgUrl: "https://img2.baidu.com/it/u=2709954499,581919391 & amp;fm=253 & amp;fmt=auto & amp;app=138 & amp;f=JPEG?w=468 & amp;h=518"
},
{
imgUrl: "https://img0.baidu.com/it/u=1462004956,1440895436 & amp;fm=253 & amp;fmt=auto & amp;app=138 & amp;f=JPEG?w=500 & amp;h=353"
}
]
      }
  },
  methods: {
async exportWordFile(){
            //Multiple images traverse to base64
for (let i in this. imglist) {
this.imglist[i].imgUrl = await getBase64Sync(this.imglist[i].imgUrl)
}
let data = {
name: this.name
imglist:this.imglist
}
let imgSize = {
//Control the size of the exported word image
imgurl:[200, 200],
};
exportWord("/My template.docx", data, `${this.listname}.docx`, imgSize);
}
  }
}
</script>