Docx 可以帮助我们实现ts/js代码生成*.docx文件的功能,它可以同时被运用于Node端和浏览器端。使用docx基本可以实现大多数Microsoft Word文档英文版的绝大数API操作,但由于Microsoft Word文档中英文版本的差异,对于中文版本的功能、属性控制还是得需要通过自定义xml的方式来实现。本篇文章简单介绍下如何使用docxjs实现word文档的生成。
可访问官网查看官方文档:https://docx.js.org/ 。我始终坚持认为,官方文档是最能帮助开发者的。
安装
$ npm install -s docx
基本用法
import * as fs from "fs"; import { Document, Packer, Paragraph, TextRun } from "docx"; // 文档类Document中包含章节数组sections,可向其中添加多个章节 const doc = new Document({ sections: [{ properties: {}, children: [ new Paragraph({ children: [ new TextRun("Hello World"), new TextRun({ text: "Foo Bar", bold: true, }), new TextRun({ text: "\tGithub is the best", bold: true, }), ], }), ], }], }); // 通过fs实现将docx从buffer流转化成*.docx文件 Packer.toBuffer(doc).then((buffer) => { fs.writeFileSync("My Document.docx", buffer); }); // 完成下载
通过上方的示例,可以发现docx将整个文档拆分成多个区域块,然后通过组合、包含区域块的方式实现文档的输出。下面,简单介绍下docx中常用的几个模块类。
Document
Document可以说是docx梦开始的地方。其实,我们也可以将其理解为docx文档本生。一个docx文档有且仅有一个document生成。可以通过如下方式生成一个基本的document对象。
const doc = new docx.Document();
document很重要,包含的属性有如下:
- creator
- description
- title
- subject
- keywords
- lastModifiedBy
- revision
- externalStyles
- styles
- numbering
- footnotes
- hyperlinks
- background
- sections
这里重点介绍externalStyles、styles。
externalStyles
这个属性对于我们的中文用户来说尤为重要,这或许也是docx作者考虑可能属性覆盖不全,所以特意留下的一个自定义属性样式的窗口。功能可以说是及其省事。我们完全可以通过解压docx文档,将其中的xml属性配置文件拿出来做些微的修改来实现我们的功能。大致就像下面这样。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <w:styles xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" mc:Ignorable="w14 w15"> <w:docDefaults> <w:rPrDefault> <w:rPr> <w:rFonts w:asciiTheme="minorHAnsi" w:eastAsiaTheme="minorHAnsi" w:hAnsiTheme="minorHAnsi" w:cstheme="minorBidi"/> <w:sz w:val="22"/> <w:szCs w:val="22"/> <w:lang w:val="en-GB" w:eastAsia="en-US" w:bidi="ar-SA"/> </w:rPr> </w:rPrDefault> <w:pPrDefault> <w:pPr> <w:spacing w:after="160" w:line="259" w:lineRule="auto"/> </w:pPr> </w:pPrDefault> </w:docDefaults> <w:style w:type="paragraph" w:default="1" w:styleId="Normal"> <w:name w:val="Normal"/> <w:qFormat/> <w:rsid w:val="000D2D99"/> <w:rPr> <w:rFonts w:ascii="MV Boli" w:hAnsi="MV Boli"/> <w:color w:val="C45911" w:themeColor="accent2" w:themeShade="BF"/> </w:rPr> </w:style> <w:style w:type="paragraph" w:styleId="Heading1"> <w:name w:val="heading 1"/> <w:basedOn w:val="Normal"/> <w:next w:val="Normal"/> <w:link w:val="Heading1Char"/> <w:uiPriority w:val="9"/> <w:qFormat/> <w:rsid w:val="000D2D99"/> <w:pPr> <w:keepNext/> <w:keepLines/> <w:spacing w:before="240" w:after="0"/> <w:outlineLvl w:val="0"/> </w:pPr> <w:rPr> <w:rFonts w:ascii="Impact" w:eastAsiaTheme="majorEastAsia" w:hAnsi="Impact" w:cstheme="majorBidi"/> <w:color w:val="538135" w:themeColor="accent6" w:themeShade="BF"/> <w:sz w:val="32"/> <w:szCs w:val="32"/> </w:rPr> </w:style> <w:style w:type="character" w:default="1" w:styleId="DefaultParagraphFont"> <w:name w:val="Default Paragraph Font"/> <w:uiPriority w:val="1"/> <w:semiHidden/> <w:unhideWhenUsed/> </w:style> <w:style w:type="table" w:default="1" w:styleId="TableNormal"> <w:name w:val="Normal Table"/> <w:uiPriority w:val="99"/> <w:semiHidden/> <w:unhideWhenUsed/> <w:tblPr> <w:tblInd w:w="0" w:type="dxa"/> <w:tblCellMar> <w:top w:w="0" w:type="dxa"/> <w:left w:w="108" w:type="dxa"/> <w:bottom w:w="0" w:type="dxa"/> <w:right w:w="108" w:type="dxa"/> </w:tblCellMar> </w:tblPr> </w:style> <w:style w:type="table" w:customStyle="1" w:styleId="MyCustomTableStyle"> <w:name w:val="My Custom TableStyle"/> <w:uiPriority w:val="99"/> <w:rsid w:val="00C36BA2"/> <w:pPr> <w:spacing w:after="0" w:line="240" w:lineRule="auto"/> </w:pPr> <w:rPr> <w:rFonts w:ascii="Comic Sans MS" w:hAnsi="Comic Sans MS"/> <w:szCs w:val="20"/> <w:lang w:val="en-US"/> </w:rPr> <w:tcPr> <w:shd w:val="clear" w:color="auto" w:fill="auto"/> </w:tcPr> <w:tblStylePr w:type="firstRow"> <w:rPr> <w:b/> <w:bCs/> <w:color w:val="FFFFFF" w:themeColor="background1"/> </w:rPr> <w:tblPr/> <w:tcPr> <w:tcBorders> <w:tl2br w:val="none" w:sz="0" w:space="0" w:color="auto"/> <w:tr2bl w:val="none" w:sz="0" w:space="0" w:color="auto"/> </w:tcBorders> <w:shd w:val="clear" w:color="auto" w:fill="4472C4" w:themeFill="accent1"/> </w:tcPr> </w:tblStylePr> </w:style> <w:style w:type="numbering" w:default="1" w:styleId="NoList"> <w:name w:val="No List"/> <w:uiPriority w:val="99"/> <w:semiHidden/> <w:unhideWhenUsed/> </w:style> <w:style w:type="paragraph" w:customStyle="1" w:styleId="CustomStyle"> <w:name w:val="Custom Style"/> <w:basedOn w:val="Normal"/> <w:link w:val="CustomStyleChar"/> <w:rsid w:val="00B557A5"/> <w:rPr> <w:sz w:val="72"/> </w:rPr> </w:style> <w:style w:type="character" w:customStyle="1" w:styleId="CustomStyleChar"> <w:name w:val="Custom Style Char"/> <w:basedOn w:val="DefaultParagraphFont"/> <w:link w:val="CustomStyle"/> <w:rsid w:val="00B557A5"/> <w:rPr> <w:rFonts w:ascii="MV Boli" w:hAnsi="MV Boli"/> <w:color w:val="C45911" w:themeColor="accent2" w:themeShade="BF"/> <w:sz w:val="72"/> </w:rPr> </w:style> <w:style w:type="character" w:customStyle="1" w:styleId="Heading1Char"> <w:name w:val="Heading 1 Char"/> <w:basedOn w:val="DefaultParagraphFont"/> <w:link w:val="Heading1"/> <w:uiPriority w:val="9"/> <w:rsid w:val="000D2D99"/> <w:rPr> <w:rFonts w:ascii="Impact" w:eastAsiaTheme="majorEastAsia" w:hAnsi="Impact" w:cstheme="majorBidi"/> <w:color w:val="538135" w:themeColor="accent6" w:themeShade="BF"/> <w:sz w:val="32"/> <w:szCs w:val="32"/> </w:rPr> </w:style> <w:style w:type="paragraph" w:customStyle="1" w:styleId="MyFancyStyle"> <w:name w:val="MyFancyStyle"/> <w:basedOn w:val="Normal"/> <w:link w:val="MyFancyStyleChar"/> <w:qFormat/> <w:rsid w:val="008802A5"/> <w:rPr> <w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman"/> <w:color w:val="FF0000"/> <w:sz w:val="144"/> </w:rPr> </w:style> <w:style w:type="paragraph" w:customStyle="1" w:styleId="subject"> <w:name w:val="subject"/> <w:basedOn w:val="Normal"/> <w:link w:val="subjectChar"/> <w:qFormat/> <w:rsid w:val="008802A5"/> <w:textAlignment w:val="center"/> <w:rPr> <w:rFonts w:hint="eastAsia" w:ascii="仿宋_GB2312" w:hAnsi="仿宋_GB2312" w:eastAsia="仿宋_GB2312" w:cs="仿宋_GB2312"/> <w:sz w:val="24"/> <w:szCs w:val="24"/> <w:lang w:val="en-US" w:eastAsia="zh-CN"/> <w:color w:val="FF0000"/> </w:rPr> </w:style> <w:style w:type="character" w:customStyle="1" w:styleId="subjectChar"> <w:name w:val="subject Char"/> <w:basedOn w:val="DefaultParagraphFont"/> <w:link w:val="subject"/> <w:rsid w:val="008802A5"/> <w:rPr> <w:rFonts w:hint="eastAsia" w:ascii="仿宋_GB2312" w:hAnsi="仿宋_GB2312" w:eastAsia="仿宋_GB2312" w:cs="仿宋_GB2312"/> <w:sz w:val="24"/> <w:szCs w:val="24"/> <w:lang w:val="en-US" w:eastAsia="zh-CN"/> <w:color w:val="FF0000"/> </w:rPr> </w:style> <w:style w:type="character" w:customStyle="1" w:styleId="MyFancyStyleChar"> <w:name w:val="MyFancyStyle Char"/> <w:basedOn w:val="DefaultParagraphFont"/> <w:link w:val="MyFancyStyle"/> <w:rsid w:val="008802A5"/> <w:rPr> <w:rFonts w:ascii="Times New Roman" w:hAnsi="Times New Roman"/> <w:color w:val="FF0000"/> <w:sz w:val="144"/> </w:rPr> </w:style> </w:styles>
import * as fs from "fs"; import { Document, Packer, Paragraph, TextRun } from "docx"; import { xmlStyles } from './xmlStyles'; // 文档类Document中包含章节数组sections,可向其中添加多个章节 const doc = new Document({ externalStyles:xmlStyles, sections: [{ properties: {}, children: [ new Paragraph({ children: [ new TextRun("Hello World"), new TextRun({ text: "Foo Bar", bold: true, }), new TextRun({ text: "\tGithub is the best", bold: true, }), ], }), ], }], }); // 通过fs实现将docx从buffer流转化成*.docx文件 Packer.toBuffer(doc).then((buffer) => { fs.writeFileSync("My Document.docx", buffer); }); // 完成下载
styles
styles也属于样式自定义的一种方式,但这里的方式定义方式是局限于当前docx提供的属性,我们可以在docx的section、Paragraph、Text、Header等中使用。
import * as fs from "fs"; import { Document, Packer, Paragraph, TextRun } from "docx"; /** * 获取自定义段落格式 */ function getParagraphStyles() { return [ { id: 'tips', // 温馨提示 name: 'tips', basedOn: "Normal", next: "Normal", quickFormat: true, paragraph: { border: { top: borderStyle, left: borderStyle, right: borderStyle, bottom: borderStyle } } }, { id: 'section', // 题型段落 name: 'section', basedOn: "Normal", next: "Normal", quickFormat: true, paragraph: { indent: indent, spacing: { before: '2pc' }, } }, ] } // 文档类Document中包含章节数组sections,可向其中添加多个章节 const doc = new Document({ sections: [{ properties: {}, children: [ new Paragraph({ children: [ new TextRun("Hello World"), new TextRun({ text: "Foo Bar", bold: true, }), new TextRun({ text: "\tGithub is the best", bold: true, }), ], }), ], }], }); // 通过fs实现将docx从buffer流转化成*.docx文件 Packer.toBuffer(doc).then((buffer) => { fs.writeFileSync("My Document.docx", buffer); }); // 完成下载