实战分享js生成word(docx),以及将word转成pdf解决方案分享

Posted 前端开发小陈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实战分享js生成word(docx),以及将word转成pdf解决方案分享相关的知识,希望对你有一定的参考价值。

本文将记录如何用js生成word文件,并在node服务器端将word转换成pdf。记录的代码均是在真实业务场景中使用成功的代码,没有记录中间踩坑的过程。想直接抄答案的家人们可以跳转到1.2 程序编写部分,最终效果图可在1.2 程序编写部分中4. 效果展示模块查看。

如果有更好的解决方案,也欢迎大家在评论区讨论、分享~

本文demo存放地址:github.com/ChicKo1108/…

一、DocxTemplater:使用js生成word

老铁们,话不多说,先上链接:Docxtemplater | Word, Powerpoint, Excel generation using templates in your application | docxtemplater

DocxTemplater是一个基于模板生成最终文件的插件,它通过一个简单的tag语法将预设的数据填入模板Word或者Powerpoint中,帮助开发者快速生成最终文件。

由于DocxTemplater是基于tag变量替换,得到最终的文件,因此文件的样式是非常可控的。在开发过程中,设计师只需要出一版最终的成品word,开发者将内容替换成对应的tag即可(再也不用被设计师追着还原设计稿了!)。

DocxTemplater是一个收费的库,但是它拥有免费的开源版本,对于我所涉及的业务,使用免费版本完全可以解决。

开源版包括的功能包括:

  • tag替换
  • 条件判断
  • 循环
  • 图片渲染

除免费版外,它还拥有pro plan(950€ per\\year)Entreprise plan(24500€ per\\year),具体功能大家可以前往官网查看。(但是白嫖的永远是最香的!)

由于我的业务只涉及生成word和pdf,所以本文只介绍word的相关内容,如果需要处理ppt,大家可前往官网自行学习。

1.1 模板语法

在了解模板语法之前,我们需要先创建一个tempalte.docx模板文件。

变量替换

变量使用key标签,在tempalte.docx文件中输入以下模板:

Hello, my name is name. 

然后准备以下数据:

let data = 
    name: "千万",
 

最终我们生成的docx文件将会是:

Hello, my name is 千万. 

条件判断

条件判断使用#key开始,使用/key结束,最简单的用法是使用Boolean类型数据进行填充。

Hello, my name is name.
#hasAgeI'm age years old./hasAge
#hasWeightMy weight is weight./hasWeight 

然后准备以下数据:

let data = 
    name: "千万",
    hasAge: true,
    age: 23,
    hasWeight: false,
 

最终我们生成的docx文件将会是:

Hello, my name is 千万.
I'm 23 years old. 

除了Boolean类型的数据以外,我们也可以填充其他类型:

type是否显示模块
false / 空数组不显示
非空数组显示,且将循环渲染内部元素
对象显示,且使用对象内部变量替换tag
其他真值显示

如果变量填充了数组,其实就是我们下面要介绍的循环语法,在下面一小节中再进行介绍。

在这里简单说一下填充对象的情况:

准备word模板如下:

总价格:price
#product $productName: $price /product 

准备数据如下:

let data = 
    price: 159,
    product: 
        productName: 'pencel',
        price: 1.2
    
 

最终我们生成的docx文件将会是:

总价格:159
pencel:$1.2 

循环

循环的标志与条件判断相同,但对应的变量应使用Array来填充。

#examScoreList
    exam: score
/examScoreList 

然后我们填充以下数据:

let data = 
    examScoreList: [
         exam: 数学, score: 60 ,
         exam: 语文, score: 50 ,
         exam: 英语, score: 40 ,
    ],
 

最终我们生成的docx文件将会是:

 数学: 60
    语文: 50
    英语: 40 

表格循环

值得注意的是,循环不仅仅可以循环一段普通文字,我们也可以对表格进行循环,包括:循环行和循环整个表。如果想要循环渲染多个表格,只需要在表格外面使用循环语法即可,不在此处过多赘述。下面展示循环渲染一个表中的行的写法:

上图中可以看到,我在表格的第二行中使用了循环语法进行填写,这样我们最终生成的文档中,表头和尾就不会被循环,第二行将会被多次渲染,结果如下:

图片

图片使用%image进行标注即可,对于图片的数据传入需要特殊处理,后面的部分会进行介绍。

总结

根据以上语法,我们就可以准备对应的word模板文件了,大部分场景下应该都可以满足。在准备模板的时候,固定的文案和样式直接保留在文档中即可,包括页眉、页脚,各个段落的行距、间距,文字的字体、大小等。其他需要根据真实数据渲染的值,就用标签标注上。准备好模板文件以后,就可以开始脚本函数的编写了。

PS: 要善用表格进行排版布局!

1.2 程序编写

安装所需库

npm install docxtemplater
npm install docxtemplater-image-module-free // 图片模块,没有图片需求可以不装
npm install pizzip // 处理模板文件用到,且只能使用该库 

客户端生成

1. 获取模板文件的binaryString

function getFileBinaryString(templateFile)  // templateFile是File对象
  return new Promise((resolve, reject) => 
    const reader = new FileReader();
    reader.onload = (e) => 
      resolve(e.target.result);
    
    reader.onerror = reject;
    reader.readAsBinaryString(templateFile);
  );
 

这里使用到了FileReader类,用于将模板文件转换为binaryString,需要注意浏览器的兼容性。

如果对兼容性有要求,可以是使用pizzip/utils中提供的方法getBinaryContent,但是此库对ts兼容性比较差,因此我在实际代码中使用了FileReader

import PizZipUtils from "pizzip/utils/index.js";

function loadFile(url, callback)  PizZipUtils.getBinaryContent(url, callback);  

2. 生成最终文件(无需图片)

// generate-doxc.js

import PizZip from 'pizzip';
import DocxTemplater from 'docxtemplater';

function getFileBinaryString(templateFile) 
  return new Promise((resolve, reject) => 
    const reader = new FileReader();
    reader.onload = (e) => 
      resolve(e.target.result);
    
    reader.onerror = reject;
    reader.readAsBinaryString(templateFile);
  );


export async function generateDocxFile(template, fileData) 
  return new Promise((resolve, reject) => 
    getFileBinaryString(template)
      .then(templateData => 
        const zip = new PizZip(templateData);
        const doc = new DocxTemplater()
          .loadZip(zip)
          .render(fileData); // fileData是我们需要定义好,传给docxtempale的数据。
        const out = doc.getZip().generate(
          type: 'blob',
          mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        );
        resolve(out);
      )
      .catch(reject);
  );
 

3. 准备数据,生成最终文件

接下来我们准备一个<input type="file" />的文件输入框(你也可以使用网络请求,或者任何方式拿到文件,只要最终获得二进制数据就可以),用来获取模板文件。同时准备好相应的数据,来对模板进行填充。

// App.jsx

import  saveAs  from 'file-saver';
import  generateDocxFile  from './utils/generate-docx';

const fileData = 
  intro: '国际劳动节,又称五一国际劳动节、劳动节、国际示威游行日,是纪念工人和劳工运动的斗争和成果的日子。国际劳动节是一项由国际劳工运动所推动的节日,全世界劳工和工人阶级在一般会在五朔节(5月1日)举行的庆祝节日,而美国和加拿大在9月第一个星期一举行。是世界上80多个国家的劳动节。',
  activities: [
    
      name: '阿尔及利亚',
      activity: '在阿尔及利亚,5月1日是公共假日,以庆祝劳动节。'
    ,
    
      name: '安哥拉',
      activity: '5月1日在安哥拉被承认为公共假日,称为劳动节。'
    ,
    
      name: '埃及',
      activity: '在埃及,5月1日被称为劳动节,是一个带薪的公共假期。在传统上,埃及总统会主持正式的五一节庆祝活动。'
    ,
    
      name: '加纳',
      activity: '5月1日是加纳的一个节日,属于庆祝全国所有工人。工会和劳工协会以游行的形式来庆祝劳动节。加纳也会举行阅兵式,通常由工会大会秘书长和各地区的区域秘书致辞。来自不同工作地点的工人通过条幅和衣着表明他们的公司。'
    
  ]


function App() 
  const handleFileChange = async (e) => 
    const file = e.target.files[0];
    const out = await generateDocxFile(file, fileData);
    saveAs(out, `$new Date().getTime().docx`);
  

  return (
    <div className="App">
      <input type="file" onChange=handleFileChange />
    </div>
  )


export default App; 

4. 效果展示

模板文件如下:

生成结果如下:

5. 图片处理

如果需要在模板中使用图片,我们需要安装docxtemplater-image-module-free模块。

引入了此模块后,需要在加载模板文件后,载入image模块,然后异步填入数据。

// generate-docx.js

// 将图片处理为base64,给模板使用
function convertImgToBase64(url, outputFormat)  
  return new Promise((resolve, reject) => 
    let canvas = document.createElement(
      'CANVAS',
    );
    const ctx = canvas.getContext('2d'),
      img = new Image();
    img.crossOrigin = 'Anonymous';
    img.onload = function () 
      canvas.height = img.height;
      canvas.width = img.width;
      ctx.drawImage(img, 0, 0);
      var dataURL = canvas.toDataURL(outputFormat || 'image/png');
      canvas = null;
      resolve(dataURL);
    ;
    img.onerror = function (e) 
      reject(e);
    ;
    img.src = url;
  );


const imageOpts =  // 图片的配置
  centered: false,
  getImage: function (tagValue, tagName)  // 将图片转成base64
    return new Promise((resolve) => 
      if (typeof tagValue === 'string' && base64Regex.test(tagValue)) 
        return resolve(tagValue);
       else 
        convertImgToBase64(tagValue).then((base64) => 
          return resolve(base64Parser(base64));
        );
      
    );
  ,
  // 设置图片宽高,可以根据tagName为每一张图片设置不同宽高
  getSize: function (img, tagValue, tagName) 
    // img是图片Buffer,tagValue是图片初始值,tagName是图片在模板中定义的标签key值
    return [150, 150]; // [宽, 高]
  
;

export async function generateDocxFile(template, fileData) 
  return new Promise((resolve, reject) => 
    getFileBinaryString(template)
      .then(templateData => 
        const zip = new PizZip(templateData);
        const doc = new DocxTemplater()
          .loadZip(zip)
          .attachModule(new ImageModule(imageOpts)) // 载入模块
          .compile();
        // 异步填充数据
        doc.resolveData(fileData)
          .then(() => 
            doc.render();
            const out = doc.getZip().generate(
              type: 'blob',
              mimeType:
                'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            );
            docxLists.push( file: out, fileName );
            resolve();
          );
      )
      .catch(reject);
  );
 

对于有图片的文档生成,需要异步载入数据,且图片数据需要处理为base64,上述代码给出了处理图片的一种解决方案,如果大家有更高效的方法也可以自行使用。

node服务器端生成

该库同样支持在node中使用,其思想与在浏览器端基本一致,在node端可以直接使用buffer,下面贴出官方给出的代码示例。

const PizZip = require("pizzip"); 
const Docxtemplater = require("docxtemplater"); 
const fs = require("fs");
const path = require("path"); // Load the docx file as binary content

const content = fs.readFileSync( path.resolve(__dirname, "input.docx"), "binary" );
const zip = new PizZip(content);
const doc = new Docxtemplater(zip,

    paragraphLoop: true,
    linebreaks: true,
); // Render the document (Replace first_name by John, last_name by Doe, ...)

doc.render(
    first_name: "John",
    last_name: "Doe",
    phone: "0652455478",
    description: "New Website",
);

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

// buf is a nodejs Buffer, you can either write it to a file or res.send it with express for example.
fs.writeFileSync(path.resolve(__dirname, "output.docx"), buf); 

1.3 总结

docxTemplater是一个通过模板文件生成word的库,它能最大程度的保证最终生成的word的样式的完整和还原。代码搭建好后,对于类似的业务,开发者们只需要编写更多的模板文件,并且把精力集中在对数据的处理上即可。

配合e-charts或其他图表库,也可以让我们实现报表文件的生成。

此外,对于pizzip这个库,它本身是对jszip库的一个升级,拥有对zip文件的操作能力,可以直接解压或者生成zip包,我们可以直接通过此库对批量生成的文件进行打包处理,打包主要用的api如下:

const zip = new pizZip();
zip.file(fileName, fileBuffer); // 生成的文件名 以及 文件的 arrayBuffer
zip.generate( type: 'blob' ), `documents.zip`); // 生成zip文件 

二、使用libre office将word转换成pdf

在进行此部分业务时,原本想在前端把所有的工作都做好,但是没有找到在客户端就直接转换的方法。因此,此部分在服务器端进行解决。

首先需要在机器上安装libre office软件,具体方法可以自行搜索。

安装好后,项目中安装libreoffice-convert库,这个库对libre office的转换方法进行了封装,直接调用其中方法就好:

const path = require('path');
const fs = require('fs');
const libre = require('libreoffice-convert');

async function docx2pdf(docxBuf, outputPath) 
  libre.convert(docxBuf, '.pdf', undefined, (err, outputBuf) => 
      if (err) 
        console.log(`Error converting file: $err`);
      
      fs.writeFileSync(outputPath, outputBuf);
   );


const inputBuf = fs.readFileSync(path.join(__dirname, 'sample.docx'));
let outputPath = path.join(__dirname, 'sample.pdf');

docx2pdf(inputBuf, outputPath); 

如果是zip文件,同样可以安装jsZip或者pizZip进行解压、打包等处理。这里更推荐使用jsZip,因为文档更加丰富,且对ts支持更好。

三、结束语

这是我第二次遇到此类业务,所以本着学习、记录、分享的心态,将内容分享到平台上。在开发过程中遇到了很多“坑”,并没有在本文中记录。本文主要还是以记录最终成功的代码为主,把内容分享给其他有同样需求的家人们。毕竟轮子已经这么完善了,当然要好好利用啦!

利用Python-docx 读写 Word 文档中的正文表格段落字体等

前言:

前两篇博客介绍了 Python 的 docx 模块对 Word 文档的写操作,这篇博客将介绍如何用 docx 模块读取已有 Word 文档中的信息。

本篇博客主要内容有:

1、获取文档的章节信息;
2、获取段落文本信息;
3、获取表格内文本信息;
4、获取文档内格式信息。

1、获取文档章节信息:

1、用docx模块获取已有的word文档对象:

from docx import Document

# Document 类,不仅可以新建word文档,也可以打开一个本地文档
doc = Document(‘test03.docx‘)  # 想获取的文档文件名,这里是相对路径。

用做示例的 test03.docx 文件截图如下:

技术图片
文档中有两个章节共两页(一个章节一页),一个章节的页面为 A3 ,另一个为 A4。

2、获取文档章节信息:

# 获取文档所有章节
sections = doc.sections
"class Sections(Sequence):"
print(sections)
# <docx.section.Sections object at 0x000000000B2E1148>
# 查看章节数量
print(len(sections)) # 2

注意:
章节对象的概念虽然比段落对象大,章节对象可以设置本章节的页面大小页眉页脚等,在该章节的段落对象必须遵守该章节的设置。
但是又并不存在包含关系,也就是说不能通过章节对象获取到段落信息。

3、获取页边距等信息:

# 获取章节对象的页边距等信息
sec0 = sections[0]
"class Section(object):"
‘‘‘
官方解释:文档节,提供对节和页面设置的访问。
还提供对页眉和页脚的访问。
‘‘‘
print(sec0)
# <docx.section.Section object at 0x000000000B2D5708>
# 获取章节页面信息
# 获取页面边距值:(单位为像素)
print(‘左边距:‘,sec0.left_margin)
# 左边距: 914400
print(‘右边距:‘,sec0.right_margin)
# 右边距: 914400
print(‘上边距:‘,sec0.top_margin)
# 上边距: 1143000
print(‘下边距:‘,sec0.bottom_margin)
# 下边距: 1143000
print(‘页眉边距:‘,sec0.header_distance)
# 页眉边距: 540385
print(‘页脚边距:‘,sec0.footer_distance)
# 页脚边距: 629920
print(‘页面方向:‘,sec0.orientation)
# 页面方向: LANDSCAPE (1)
print(‘页面高度:‘,sec0.page_height)
# 页面高度: 10657205
print(‘页面宽度:‘,sec0.page_width)
# 页面宽度: 15085695

这里获取的是第一个章节的页面信息,也就是 A3 页面的信息。

注:上一篇博客 往Word文档中插入图片、表格,设置表格样式,章节,页眉页脚等,在介绍设置页面信息时,忘记介绍如何设置页面方向了,这里补充一下:

设置页面方向:

# 导入设置页面方向所需模块
from docx.enum.section import WD_ORIENT
# 获取章节对象
section = document.sections[0]
# 设置页面方向
section.orientation = WD_ORIENT.LANDSCAPE # 横向

可设置项有横向( LANDSCAPE ) 和纵向 ( PORTRAIT ):

‘‘‘
‘PORTRAIT‘, 0, ‘portrait‘, ‘Portrait orientation.‘ 
‘LANDSCAPE‘, 1, ‘landscape‘, ‘Landscape orientation.‘
‘‘‘

文档截图:

技术图片
4、获取文档页眉页脚信息:

head0 = sec0.header # 获取页眉对象
print(head0)
# <docx.section._Header object at 0x000000000B2E1348>
head0_pars = head0.paragraphs # 获取 页眉 paragraphs
# 获取页眉文字信息
# 因存在多个 paragraph 对象的可能所以用循环读取的方式
head0_string = ‘‘
for par in head0_pars:
    head0_string += par.text

print(head0_string)
# 仪征市马集镇总体规划(2017-2030)——说明

# 获取页脚信息,也是类似的方法
foot0 = sec0.footer
print(foot0) # 获取页脚对象
# <docx.section._Footer object at 0x000000000B2E3808>
foot0_pars = foot0.paragraphs
foot0_string = ‘‘
for par in foot0_pars:
    foot0_string += par.text
print(foot0_string)
# 1

2、获取段落文字信息:

1、获取Word文档所有段落对象:

"获取文档所有段落信息:"
# 获取文档所有段落对象
paragraphs = doc.paragraphs

注意:
paragraphs 获取的是文档中所有段落对象的列表,严格来说是word文档中正文部分的段落对象列表。因为通过前文的介绍,许多除正文部分,如 表格,页面页脚等元素也包含 paragraph 对象。
而 doc.paragraphs 获取到的 paragraph 不包含这些段落对象。

2、获取段落对象文字信息:

print(paragraphs)
print(len(paragraphs))  # 打印结果:20

# 获取一个段落对象的文字信息
par0 = paragraphs[0]
print(par0)
par0_string = par0.text
print(par0_string)

# 获取所有段落文字信息
pars_string = [par.text for par in paragraphs]
print(pars_string)

par0_string 打印截图:

技术图片
3、获取段落格式信息:

print(‘段落对齐方式:‘,par0.paragraph_format.alignment)
# 段落对齐方式: LEFT (0)
print(‘左缩进:‘,par0.paragraph_format.left_indent)
# 左缩进: None
print(‘右缩进:‘,par0.paragraph_format.right_indent)
# 右缩进: None
print(‘首行缩进:‘,par0.paragraph_format.first_line_indent)
# 首行缩进: 304800
print(‘行间距:‘,par0.paragraph_format.line_spacing)
# 行间距: 1.5
print(‘段前间距:‘,par0.paragraph_format.space_before)
# 段前间距: 198120
print(‘段后间距:‘,par0.paragraph_format.space_after)
# 段后间距: 198120

LIK1

 

3、获取文字格式信息:

paragraph 对象 里还有更小的 run 对象,run 对象才包含了段落对象的文字信息。
paragraph.text 方法也是通过 run 对象的方法获取到文字信息的:

paragraph.text 方法源码:

def text(self):
     text = ‘‘
        for run in self.runs:
            text += run.text
        return text

文字的字体、大小、下划线等信息都包含在 run 对象中(不清楚的看前面的博客):

# 获取段落的 run 对象列表
runs = par0.runs
print(runs)
# 获取 run 对象
run_0 = runs[0]
print(run_0.text) # 获取 run 对象文字信息
# 打印结果:
# 坚持因地制宜,差异化打造特色小镇,

文档 段落 和 run 对象示意:

技术图片
获取文字格式信息:

# 获取文字格式信息
print(‘字体名称:‘,run_0.font.name)
# 字体名称: 宋体
print(‘字体大小:‘,run_0.font.size)
# 字体大小: 152400
print(‘是否加粗:‘,run_0.font.bold)
# 是否加粗: None
print(‘是否斜体:‘,run_0.font.italic)
# 是否斜体: True
print(‘字体颜色:‘,run_0.font.color.rgb)
# 字体颜色: FF0000
print(‘字体高亮:‘,run_0.font.highlight_color)
# 字体高亮: YELLOW (7)
print(‘下划线:‘,run_0.font.underline)
# 下划线: True
print(‘删除线:‘,run_0.font.strike)
# 删除线: None
print(‘双删除线:‘,run_0.font.double_strike)
# 双删除线: None
print(‘下标:‘,run_0.font.subscript)
# 下标: None
print(‘上标:‘,run_0.font.superscript)
# 上标: None
LIK2

注:前面的博客好像也没介绍文字背景颜色的设置…

背景颜色设置方法: (与字体颜色设置方法有区别)

# 设置背景颜色
from  docx.enum.text import WD_COLOR_INDEX
run_2.font.highlight_color = WD_COLOR_INDEX.YELLOW

背景颜色可选值有:

‘‘‘
‘AUTO‘, 0, ‘default‘
‘BLACK‘, 1, ‘black‘
‘BLUE‘, 2, ‘blue‘
‘BRIGHT_GREEN‘, 4, ‘green‘,
‘DARK_BLUE‘, 9, ‘darkBlue‘,
‘DARK_RED‘, 13, ‘darkRed‘
‘DARK_YELLOW‘, 14, ‘darkYellow‘
‘GRAY_25‘, 16, ‘lightGray‘
‘GRAY_50‘, 15, ‘darkGray‘
‘GREEN‘, 11, ‘darkGreen‘
‘PINK‘, 5, ‘magenta‘
‘RED‘, 6, ‘red‘
‘TEAL‘, 10, ‘darkCyan‘
‘TURQUOISE‘, 3, ‘cyan‘
‘VIOLET‘, 12, ‘darkMagenta‘
‘WHITE‘, 8, ‘white‘
‘YELLOW‘, 7, ‘yellow‘
‘‘‘LIK3

4、获取文档中表格信息:

示例文档中表格截图:
技术图片
1、获取表格样式、单元格对象及文字信息:

# 获取文档中表格信息
tables = doc.tables # 获取文档中所有表格对象的列表
print(tables)
# [<docx.table.Table object at 0x000001957059CD48>]
print(len(tables)) # 查看文档中表格数量
# 1
table0 = tables[0]  # 获取表格对象

# 获取表格的样式信息
print(table0.style)
# _TableStyle(‘Normal Table‘) id: 190621384

# 获取一个表格的所有单元格
cells = table0._cells
print(len(cells)) # 表格中单元格数量
# 15

# 获取单元格内所有文字信息
cells_string = [cell.text for cell in cells]
print(cells_string)
LIK4

cells_string 打印截图:

技术图片
从截图中可以看出来,用 tableobj._cells 获取到的单元格对象列表是按行排列的。

2、获取表格的行列对象:

# 获取表格对象行数量、列数量
col_num = len(table0.columns)
print(col_num) # 3
# 行数量
row_num = len(table0.rows)
print(row_num) # 5

# 获取行对象
row0 = table0.rows[0]
# 获取列对象
col0 = table0.columns[0]

# 获取行对象文字信息
‘要用 row0.cells 获取行对象的 cell 才能获取其文字信息‘
row0_string = [cell.text for cell in row0.cells]
print(row0_string)

# 获取列对象文字信息
col0_string = [cell.text for cell in col0.cells]
print(col0_string)
LIK5

注:在介绍单元格格式信息时,说过单元格内文字信息也是通过 run 对象设置,故获取文字信息也和前面获取段落文字信息类似,就不重复讲了。

结尾:

以上就是本篇博客的全部内容了,后面将会用两个实例来将前面三篇博客的知识点串联起来,感谢阅读。

【Python与Office】专栏

该专栏会对 Python 的第三方模块,如:xlwt,xlrd,python-docx等,操作 Office 办公软件(Word Excel PPT)的方法进行详细讲解。同时也会搭配一些实例演练,一方面强化知识点的理解与运用,另一方面也希望能起到,引导读者进行思考:如何用 python 提高 offic 办公软件办公效率的作用。

 

感兴趣的朋友,可以点个 关注收藏 。如在博客中遇到任何问题或有一些想法、需求,可留言或私信。

 

创作不易,你的支持是我最大的动力,感谢 !

以上是关于实战分享js生成word(docx),以及将word转成pdf解决方案分享的主要内容,如果未能解决你的问题,请参考以下文章

如何通过java将多个word文档合成一个wor

使用apache的POI API 生成word文档(docx)时,怎么将一段文字设置成为大纲标题,如设置成标题1

办公自动化:几行代码将PDF文档转换为WORD文档(代码实战)!

java用模板生成word(docx)文档(含动态表格)

Word转PDF

poi-tl——Word模板生成器