import JSZipUtils from 'jszip-utils'; import JSZip from 'jszip'; import docxtemplater from 'docxtemplater'; import ImageModule from 'docxtemplater-image-module-free'; import Pizzip from 'pizzip'; import FileSaver from 'file-saver'; import { renderAsync } from 'docx-preview'; import printJS from 'print-js'; import pdfMake from 'pdfmake/build/pdfmake'; import vfs from 'pdfmake/build/vfs_fonts.js'; import { jsPDF } from 'jspdf'; /** * 等比例缩放图片 * 根据图片的长宽比进行不同方式的缩放 * 如果宽度大于高度(横拍图片),则按照设定高度等比缩放; * 如果宽度小于高度(竖拍图片),则按照设定宽度等比缩放; * @param {Number} horizontalHeight 设定高度 * @param {Number} verticalWidth 设定宽度 * @param {*} img * @param {*} tagValue * @param {*} tagName * @returns */ function getSizeProportional( horizontalHeight, verticalWidth, img, tagValue, tagName ) { return new Promise(function (resolve, reject) { const image = new Image(); image.src = tagValue; image.onload = function () { let width = image.width; let height = image.height; // console.log('width height', width, height); if (width > height && horizontalHeight && height > horizontalHeight) { const scale = image.height / horizontalHeight; height = horizontalHeight; width = image.width / scale; } else if (width <= height && verticalWidth && width > verticalWidth) { const scale = image.width / verticalWidth; width = verticalWidth; height = image.height / scale; } // console.log('scale', width, height); resolve([width, height]); }; image.onerror = function (e) { console.log('img, tagValue, tagName : ', img, tagValue, tagName); alert('An error occured while loading ' + tagValue); reject(e); }; }); } /** * 固定大小缩放图片 * 根据图片的长宽比进行不同方式的缩放 * 如果宽度大于高度(横拍图片),则按照设定高度和宽高比缩放; * 如果宽度小于高度(竖拍图片),则按照设定宽度和宽高比缩放; * @param {*} horizontalHeight 设定高度 * @param {*} verticalWidth 设定宽度 * @param {*} scale 缩放比例,长边除短边的比例系数 * @param {*} img * @param {*} tagValue * @param {*} tagName * @returns */ function getSizeFixed( horizontalHeight, verticalWidth, scale, img, tagValue, tagName ) { return new Promise(function (resolve, reject) { const image = new Image(); image.src = tagValue; image.onload = function () { let width = image.width; let height = image.height; if ( width > height && horizontalHeight && height > horizontalHeight && scale ) { height = horizontalHeight; width = horizontalHeight * scale; } else if ( width <= height && verticalWidth && width > verticalWidth && scale ) { width = verticalWidth; height = verticalWidth * scale; } resolve([width, height]); }; image.onerror = function (e) { console.log('img, tagValue, tagName : ', img, tagValue, tagName); alert('An error occured while loading ' + tagValue); reject(e); }; }); } /** * 获取图片配置信息 * @param {Number} horizontalHeight 图片宽度大于高度时,限制其高度 * @param {Number} verticalWidth 图片宽度小于高度时,限制其宽度 * @returns */ function getImageOptions(options) { const horizontalHeight = options ? options.horizontalHeight : undefined; const verticalWidth = options ? options.verticalWidth : undefined; return { centered: false, fileType: 'docx', getImage(tagValue) { // In this case tagValue will be a URL tagValue = "https://docxtemplater.com/puffin.png" return new Promise(function (resolve, reject) { JSZipUtils.getBinaryContent(tagValue, function (error, content) { if (error) { return reject(error); } return resolve(content); }); }); }, getSize(img, tagValue, tagName) { // return getSizeProportional(horizontalHeight, verticalWidth, img, tagValue, tagName) return getSizeFixed( horizontalHeight, verticalWidth, options.scale, img, tagValue, tagName ); } }; } function prepareDocxtemplater(tempDocpath, data, imageSize) { return new Promise((resolve, reject) => { JSZipUtils.getBinaryContent(tempDocpath, (error, content) => { if (error) { reject(error); throw error; } const zip = new Pizzip(content); let doc = new docxtemplater() .setOptions({ paragraphLoop: true }) .loadZip(zip); if (imageSize) { const imageOptions = getImageOptions(imageSize); doc.attachModule(new ImageModule(imageOptions)); } doc.compile(); doc.resolveData(data).then(() => { resolve(doc); }); }); }); } /** * 准备docx文档,返回blob对象 * @param {*} tempDocpath 模板docx文件路径 * @param {*} data 数据对象 * @param {*} zipName 压缩包名称 * @param {*} imageSize 图片大小配置对象 * @returns */ function prepareDocxBlob(tempDocpath, data, imageSize) { return prepareDocxtemplater(tempDocpath, data, imageSize).then((doc) => { try { doc.render(); } catch (error) { console.log(error); throw error; } const out = doc.getZip().generate({ type: 'blob', mimeType: 'application/vnd.openxmlformats-officedocumnet.wordprocessingml.document' }); return out; }); } function prepareDocxStr(tempDocpath, data, imageSize) { return prepareDocxtemplater(tempDocpath, data, imageSize).then((doc) => { try { return doc.render(); } catch (error) { console.log(error); throw error; } }); } async function preparePdf(tempDocpath, data, imageSize) { // return prepareDocxStr(tempDocpath, data, imageSize).then((res) => { // // 将 Word 文档转换为 PDF 格式并保存到文件 // console.log(pdfMake); // console.log('res', res); // // const printer = new PdfPrinter(vfs); // const docDefinition = { // content: [ // { // text: res.getZip().generate({ // type: 'blob', // mimeType: // 'application/vnd.openxmlformats-officedocumnet.wordprocessingml.document' // }) // // text: res.toString('utf8') // } // ] // }; // const pdfDoc = pdfMake.createPdf(docDefinition); // // const pdfBuffer = await pdfDoc.getBuffer(); // // return new Blob([pdfBuffer], { type: 'application/pdf' }); // console.log(pdfDoc); // return pdfDoc; // // console.log('res', res.toString('utf8')); // // const doc = new jsPDF(); // // doc.text(res.toString('utf8'), 10, 10); // // return doc; // }); try { // 第一步:使用docxtemplater处理docx模板 const doc = await prepareDocxStr(tempDocpath, data, imageSize); // 第二步:获取渲染后的docx内容 const docxBlob = doc.getZip().generate({ type: 'blob', mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }); return docxBlob // 第三步:读取docx内容(注意:这一步需要额外的库来解析docx内容) // 由于pdfmake不能直接处理docx格式,我们需要先解析docx内容 // 这里提供两种方案: // 方案1:使用docx-preview库先渲染为HTML,再用pdfmake创建PDF // const container = document.createElement('div'); // await renderAsync(docxBlob, container); // // 提取HTML内容并转换为pdfmake可用的格式 // const textContent = extractTextFromHtml(container.innerHTML); // // 第四步:使用pdfmake创建PDF // const docDefinition = { // content: [ // { // text: textContent, // style: 'body' // } // ], // }; // const pdfDoc = pdfMake.createPdf(docDefinition); // return pdfDoc; } catch (error) { console.error('转换docx到pdf失败:', error); throw error; } } /** * 从HTML中提取文本内容 * @param {string} html HTML字符串 * @returns {string} 提取的文本内容 */ function extractTextFromHtml(html) { const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; return tempDiv.textContent || tempDiv.innerText || ''; } function exportDocx(tempDocpath, data, zipName, imageSize) { return prepareDocxBlob(tempDocpath, data, zipName, imageSize).then((blob) => { FileSaver.saveAs(blob, zipName); }); } function previewDocx(blob, ref) { return renderAsync(blob, ref); } function downloadDocx(blob, zipName) { FileSaver.saveAs(blob, zipName); } function print({ ref, title, style, type, jsonData, borderHeadStyle, gridStyle, css }) { printJS({ printable: ref, header: title || null, type: type || 'html', headerStyle: 'font-size:6px;font-weight:600;text-align:center;padding:15px 0 10px 0;', //标题设置 properties: jsonData || [], //json数据元 // gridHeaderStyle: // borderHeadStyle || // 'font-size:6px;font-weight:400;height:40px;line-height:40px;border: 1px solid #ccc;padding:3px 5px 3px 5px;text-align:center;', //json格式表头样式 // gridStyle: // gridStyle || // 'font-size:1px;font-weight:200;border: 1px solid #ccc;padding:3px 5px 3px 5px;text-align:center;', //json各式表哥央视 scanStyles: false, //不适用默认样式 repeatTableHeader: false, //打印json表头只显示在第一页 style: style || `@page{size:A4;margin: -1cm 0cm 0cm 0cm;}`, //去除页眉页脚 css: css || null //css url }); } export { exportDocx, prepareDocxBlob, preparePdf, previewDocx, downloadDocx, print };