preface

Why insert a “chart” in Word? Docx, a third-party library known to the front-end, does not support inserting “charts”!

The Docx library provides an elegant declarative API that allows you to easily generate “.docx “files using JS/TS. It also supports both Node.js and browsers.

In order to solve this problem, I want to talk about it through this article, I hope you can not only realize the function of exporting Word, but also realize the insertion of “chart” in the exported Word! This article is mainly about how to insert “chart” in the exported Word. After a long technical implementation, step by step from the front end to the back end, from problem derivation to technical optimization. The main body is divided into the following four parts

  1. React is implemented using echarts’ APIgetDataURL

  2. Node.js is implemented using the puppeteerheadless browser snapshot function

  3. Node.js implemented with Canvas (recommended)

  4. Golang implements word export and insert “chart” via template rendering text/ Template (highly recommended)

Common APIS for Docx

The Docx library provides many classes for developers to create corresponding elements in Word. Here we briefly introduce a few common classes:

  • Document: used to create a new Word Document;
  • Sections: Used to create blocks. A block contains one or more Sections.
  • Paragraph: Used to create new paragraphs;
  • Text Run: Used to create Text, supporting bold, italic, and underline styles.
  • Image Run: used to create images and support floating and inline positioning.
  • Tables: Used to create Tables that support setting the contents of each row and each table cell.

React is implemented via echarts’ getDataURL

Docx cannot insert “charts”, but thankfully images can be inserted. Image Run can insert images through DataURL and buffer, so I think echarts’ “getDataURL” can get the DataURL of the chart. Follow the following method to achieve the “chart” image Data URL generation.

/ * * *@descriptionGenerate the chart Data URL * from echarts' instance API@param {option} Option configuration for echarts chart *@return {string} Data URL
 */
const getChartDataURL = (option = {}) = > {
  const container = document.createElement("div")
  container.id = "container"
  container.style.display = "none"
  container.style.width = "500px"
  container.style.height = "500px"
  document.body.appendChild(container)
  const instance = echarts.init(container)
  // Draw a diagram
  // The following are the reasons for the initial inability to draw a graph:
  // If there is an animation, the generated image will look as it did before the animation came out, that is, the data has not been rendered, so the exported image has no data.
  // https://blog.csdn.net/qq_35239421/article/details/106526147
  instance.setOption(option)
  const dataUrl = instance.getDataURL({ type: "png" })
  document.body.removeChild(container)
  return dataUrl
}
Copy the code

Complete project reference code stamp here, the code can be run directly to view, the same as below!

Node.js is implemented using the puppeteer Headless browser snapshot function

Echarts is only available in the browser (of course you can also use the echarts library wrapped in the back end, I didn’t implement this, but you can link to your implementation in the comments section!) How to do ah! There’s a Headless browser on the back end. Puppeteer snapshots are as follows:

/** * Generate echarts image **@param {*} Option echarts Option * *@returns Image buffer * */
async function getEchartsChart(option = {}) {
  // Start the browser
  const browser = await puppeteer.launch({
    args: ["--no-sandbox"],})// Create a blank page
  const page = await browser.newPage()
  try {
    // Define a web page template
    const content = ` 
         
       
       chart   
      
`
// Set the page source code await page.setContent(content) // Add script tags and attributes await page.addScriptTag({ path: path.resolve( __dirname, "./node_modules/echarts/dist/echarts.min.js"})),// Execute javascript code in web page await page.evaluate(option= > { const myChart = window.echarts.init(document.getElementById("container")) myChart.setOption(option) }, option) // Get the element whose ID is Container from the web page let elem = await page.$("#container") // Capture element snapshot let buffer = await elem.screenshot({ type: "png".Resolve (__dirname, './123.png') // Snapshot generation path }) return buffer } catch (err) { console.log(`render echarts chart error: ${err}`) return null } finally { // Close the page await page.close() // Close the browser await browser.close() } } Copy the code

The full project reference code is here

Node.js is implemented using canvas

Having to go through the Headless browser to get a snapshot of the chart every time, one request for each headless browser, is both inefficient and performance costly. This can be optimized by using a separate Canvas library set as an Echarts container, as follows:

const canvas = require("canvas")
const echarts = require("echarts")
const fs = require("fs")

/** * Generate echarts image **@param {*} Option echarts Option * *@returns Image buffer * */

async function getBufferByCanvas(option = {}) {
  // Create a canvas instance
  let ctx = canvas.createCanvas(500.500)
  // Set the Canvas instance as an Echarts container
  echarts.setCanvasCreator(() = > ctx)
  Create an echarts instance for the container using the Canvas instance
  let chart = echarts.init(ctx)
  // Set the icon instance configuration item
  chart.setOption(option)
  return chart.getDom().toBuffer()
}
Copy the code

The full project reference code is here

Golang implements Word export and insert “chart” functionality through template rendering

  • Why do we use Go? Node.js can export and insert charts in the same way, since Go is used at the back end of our project. Another reason is: Go is really fast!
  • Every time you insert a “chart” it feels like an inserted picture, so what’s the point of exporting Word? Graphs can only be viewed, not modified or interacted with (→_→). Okay, I see that little look in your eyes. Here’s how to render the template to help you solve all those questions.

“. Docx “features

Docx word is a zip file. You can rename example.docx to example.zip and unzip it to see the following file:

  • [Content_Types].xmlThis file is used to define the content type of each XML file.
  • _rels: This directory typically has a file with the suffix “.rels, “which holds the relationships between parts in the directory._relsThere’s more than one directory, it’s actually hierarchical;
  • docProps: The XML file in this directory is used to save the attributes of docx files.
  • word: This directory contains information such as the content, font, style, or theme of a Word document.

Word folder is the template file we need to pay attention to and modify as our template file, you can see the folder directory is as follows:

  • chartsIt’s a collection of graphs
  • document.xmlIs an XML display of the entire document

Template rendering implements Word export

Take the word document below as an example

  1. One request creates oneResult Template folder

  1. theSource Template folderWhere data needs to be inserted into theTemplate syntaxThis is the template syntax in Go{{.Title}}, through theSource Template folderCombined with the dataApply colours to a drawingOut the final file replacementResult Template folderFile with the same name in. Like this one right here/word/charts/chart1.xmlFile (same with other XML files)
<c:cat>
    <c:strRef>
        <c:f>Sheet1! $A$2:$A$5</c:f>
        <c:strCache>
            <c:ptCount val="{{.ChartData | len | print}}" />{{/ * traversal data ChartData: [] Chart {{" Ming ", 100}, {" flower ", 88}, {" little red ", 66}} * /}} {{$index range, $element: = ChartData}}<c:pt idx="{{$index | print}}">
                {{ /* name */}}
                <c:v>{{$element.Key}}</c:v>
            </c:pt>
            {{end}}
        </c:strCache>
    </c:strRef>
</c:cat>
<c:val>
    <c:numRef>
        <c:f>Sheet1! $B$2:$B$5</c:f>
        <c:numCache>
            <c:formatCode>General</c:formatCode>
            <c:ptCount val="{{.ChartData | len | print}}"/>
            {{range $index, $element := .ChartData}}
            <c:pt idx="{{$index | print}}">
                {{/* value */}}
                <c:v>{{$element.Value}}</c:v>
            </c:pt>
            {{end}}
        </c:numCache>
    </c:numRef>
</c:val>
Copy the code
  1. Finally, theResult Template folderZip, then rename to.docx. You can go straight back to the front end. The final rendered Word document is shown below

The advantages and disadvantages

  • Advantages:
    • The diagrams here can be interacted and modified!
    • Performance is also good and there is no reliance on other third-party libraries.
    • You can modify the word style according to the corresponding XML (Docx also works)
  • Disadvantages:
    • Each request generates a decompressed template folder, which can take up a lot of memory if there are too many requests.

The full project reference code is here

reference

  • How to play Word documents in the front end
  • Node + Echarts implements server-side rendering of visual charts