@

如何通过ECharts在后台生成图片,然后插入到Word文档中?

首先要解决一个问题:总所周知,ECharts是前端的一个图表库,如何在后台调用JS代码?这里就要用到PhantomJS了。

PhantomJS是一个基于WebKit的JavaScript API,它使用QtWebKit作为核心浏览器的功能,使用WebKit编译、解释和执行JavaScript代码。任何可以在基于WebKit的浏览器上做的事情,它都能做到。它不仅是一个隐形的浏览器,提供了诸如CSS选择器、支持Web标准、DOM操作、JSON、HTML5、Canvas、SVG等功能,同时也提供了处理文件I/O的操作。

之前写过一个文档模板工具,其中可以通过占位符插入图片。

用PhantomJS生成ECharts图表的Png图片,利用文档模板工具插入图片即可实现这个需求。

下面就来看看如何实现。

在后端调用JS代码

创建一个.netstandard2.1的类库项目。为了方便调用,我们安装一个PhantomJS包装器:NReco.PhantomJS

dotnet add package NReco.PhantomJS --version 1.1.0

这只是一个包装器,因此还需要一个可执行文件,前往官网下载PhantomJS。

因为直接使用编译好的可执行文件,因此需要下载对应的平台版本,这里我下载了Windows以及Linux 64-bit版本。

将下载好的可执行文件解压放置在项目根目录下的libs目录中。

这样我们可以直接在.net中调用PhantomJS了。

准备ECharts库

jQuery

下载jquery-3.6.3.min.js: https://code.jquery.com/jquery-3.6.3.min.js

ECharts

下载echarts.min.js: https://github.com/apache/echarts/tree/5.4.3/dist

ECharts转换器

echarts-convert在github上有众多版本,echarts-convert的代码来源于这里:https://github.com/wadezhan/billfeller.github.io/issues/85

这里选择

(function () {
var system = require('system');
var fs = require('fs');
var config = {
// define the location of js files
JQUERY: 'jquery-3.6.3.min.js',
//ESL: 'esl.js',
ECHARTS: 'echarts.min.js',
// default container width and height
DEFAULT_WIDTH: '1920',
DEFAULT_HEIGHT: '800'
}, parseParams, render, pick, usage; usage = function () {
console.log("\nUsage: phantomjs echarts-convert.js -options options -outfile filename -width width -height height"
+ "OR"
+ "Usage: phantomjs echarts-convert.js -infile URL -outfile filename -width width -height height\n");
}; pick = function () {
var args = arguments, i, arg, length = args.length;
for (i = 0; i < length; i += 1) {
arg = args[i];
if (arg !== undefined && arg !== null && arg !== 'null' && arg != '0') {
return arg;
}
}
}; parseParams = function () {
var map = {}, i, key;
console.log("--logs:\n")
console.log(system.args)
if (system.args.length < 2) {
usage();
phantom.exit();
}
for (i = 0; i < system.args.length; i += 1) {
if (system.args[i].charAt(0) === '-') {
key = system.args[i].substr(1, i.length);
if (key === 'infile') {
// get string from file
// force translate the key from infile to options.
key = 'options';
try {
map[key] = fs.read(system.args[i + 1]).replace(/^\s+/, '');
} catch (e) {
console.log('Error: cannot find file, ' + system.args[i + 1]);
phantom.exit();
}
} else { map[key] = system.args[i + 1].replace(/^\s+/, '');
}
}
}
return map;
}; render = function (params) {
var page = require('webpage').create(), createChart; var bodyMale = config.SVG_MALE;
page.onConsoleMessage = function (msg) {
console.log(msg);
}; page.onAlert = function (msg) {
console.log(msg);
}; createChart = function (inputOption, width, height, config) {
var counter = 0;
function decrementImgCounter() {
counter -= 1;
if (counter < 1) {
console.log(messages.imagesLoaded);
}
} function loadScript(varStr, codeStr) {
var script = $('<script>').attr('type', 'text/javascript');
script.html('var ' + varStr + ' = ' + codeStr);
document.getElementsByTagName("head")[0].appendChild(script[0]);
if (window[varStr] !== undefined) {
console.log('Echarts.' + varStr + ' has been parsed');
}
} function loadImages() {
var images = $('image'), i, img;
if (./Assets/images.length > 0) {
counter = images.length;
for (i = 0; i < images.length; i += 1) {
img = new Image();
img.onload = img.onerror = decrementImgCounter;
img.src = images[i].getAttribute('href');
}
} else {
console.log('The images have been loaded');
}
}
// load opitons
if (inputOption != 'undefined') {
// parse the options
loadScript('options', inputOption);
// disable the animation
options.animation = false;
} // we render the image, so we need set background to white.
$(document.body).css('backgroundColor', 'white');
var container = $("<div>").appendTo(document.body);
container.attr('id', 'container');
container.css({
width: width,
height: height
});
// render the chart
var myChart = echarts.init(container[0]);
myChart.setOption(options);
// load images
loadImages();
return myChart.getDataURL();
}; // parse the params
page.open("about:blank", function (status) {
// inject the dependency js
page.injectJs(config.ESL);
page.injectJs(config.JQUERY);
page.injectJs(config.ECHARTS); var width = pick(params.width, config.DEFAULT_WIDTH);
var height = pick(params.height, config.DEFAULT_HEIGHT); // create the chart
var base64 = page.evaluate(createChart, params.options, width, height, config);
//fs.write("base64.txt", base64);
// define the clip-rectangle
page.clipRect = {
top: 0,
left: 0,
width: width, height: height
};
// render the image
page.render(params.outfile);
console.log('render complete:' + params.outfile);
// exit
phantom.exit();
});
};
// get the args
var params = parseParams(); // validate the params
if (params.options === undefined || params.options.length === 0) {
console.log("ERROR: No options or infile found.");
usage();
phantom.exit();
}
// set the default out file
if (params.outfile === undefined) {
var tmpDir = fs.workingDirectory + '/tmp';
// exists tmpDir and is it writable?
if (!fs.exists(tmpDir)) {
try {
fs.makeDirectory(tmpDir);
} catch (e) {
console.log('ERROR: Cannot make tmp directory');
}
}
params.outfile = tmpDir + "/" + new Date().getTime() + ".png";
} // render the image
render(params);
}());

将上述文件放在项目根目录下的js目录中。

我们来测试一下是否能生成一个简单的ECharts图表。

创建一个option.json

首先指定一个option,在官方示例 https://echarts.apache.org/examples/zh/index.html 中,随意找一个柱状图的sample,复制option对象内容到新创建的option.json文件中

{
"tooltip": {
"trigger": "axis",
"axisPointer": {
"type": "shadow"
}
},
"grid": {
"left": "3%",
"right": "4%",
"bottom": "3%",
"containLabel": true
},
"xAxis": [
{
"type": "category",
"data": [ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" ],
"axisTick": {
"alignWithLabel": true
}
}
],
"yAxis": [
{
"type": "value"
}
],
"series": [
{
"name": "Direct",
"type": "bar",
"barWidth": "60%",
"data": [ 10, 52, 200, 334, 390, 330, 220 ]
}
]
}

Program.cs中调用ECharts转换器:

static void Main(string[] args)
{
var phantomJS = new PhantomJS(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
phantomJS.ToolPath = Path.Combine(basePath, "libs\\phantomjs-2.1.1-windows\\bin"); }
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
phantomJS.ToolPath = Path.Combine(basePath, "libs\\phantomjs-2.1.1-linux-x86_64\\bin"); }
var scriptPath = Path.Combine(basePath, "js\\echarts-converts.js"); var optionPath = Path.Combine(basePath, "js\\option.json"); phantomJS.OutputReceived += (sender, e) =>
{
Console.WriteLine("PhantomJS output: {0}", e.Data);
}; phantomJS.Run(scriptPath, new string[] { "-infile", optionPath });
phantomJS.Abort(); }

打印如下

打开输出路径看到生成的图片。

生成Word文档

为了方便集成,我加.NET中构件ECharts中可能用的全部数据结构。

这里感谢https://github.com/idoku/EChartsSDK这个项目,代码基本都是从这里拷贝过来的。

这样可以通过指定ChartOption对象,生成图片。

var option = new ChartOption()
{
title = new List<Title>()
{
new Title (){
text=title, left="center"}
},
tooltip = new ToolTip(),
legend = new Legend()
{
orient = OrientType.vertical,
left = "left"
},
series = new object[]
{
new {
name= "Access From",
type="pie",
data=new object[]
{
new { value= failedCount, name="异常" },
new { value= passCount, name="正常" },
}
}
}
}

根据Document Template Tool图片占位符格式:#字段名称[宽度,高度]#,

在上一章的Sample基础上,在ReportTemplate2.docx中添加图片占位符。

生成后的文档如下:

项目地址

库封装

https://github.com/jevonsflash/EChartsGen

本文示例 EChartsGen_DocTemplateTool_Sample

https://github.com/jevonsflash/EChartsGen/tree/master/EChartsGen_DocTemplateTool_Sample

将ECharts图表插入到Word文档中的更多相关文章

  1. aspose.words复制插入同一word文档中的某个页面

    选择word模板 Document doc = new Document(Server.MapPath("~\\templet") + "\\" + name. ...

  2. 在word文档中如何插入Mathtype公式

    将mathtype公式插入到word文档中,是mathtype公式编辑器最基本的操作.当在Mathtype数学公式编辑器中编辑好公式之后,点击文件->更新XXX文档(XXX为当前编辑的word文 ...

  3. C# Word文档中插入、提取图片,文字替换图片

    Download Files:ImageOperationsInWord.zip 简介 在这篇文章中我们可以学到在C#程序中使用一个Word文档对图像的各种操作.图像会比阅读文字更有吸引力,而且图像是 ...

  4. C#/VB.NET 在Word文档中插入分页符

    分页符是分页的一种符号,上一页结束以及下一页开始的位置.通查用于在指定位置强制分页.本文将分为两部分来介绍如何在Word文档中插入分页符.并附上C#/VB.NET以供参考,详情请阅读以下内容. 在特定 ...

  5. Aspose.Words:如何添加另一个WORD文档中的Node对象

    原文:Aspose.Words:如何添加另一个WORD文档中的Node对象 首先看一段代码,这段代码意图从docSource中获取第一个表格,并插入docTarget的末尾: , true); doc ...

  6. 处理Word文档中所有修订

    打开现有文档进行编辑 若要打开现有文档,您可以将 Word类实例化,如以下 using 语句所示. 为此,您可以使用Open(String, Boolean) 方法打开具有指定 fileName 的字 ...

  7. 详解在Word文档中常见的各种公式编辑问题

    正常情况下,我们在安装完成MathType之后会直接加载在Word文档中,Word文档中的MathType比较复杂,新手操作遇到麻烦也是常有的事,今天就来给大家详解下Word文档中常见的MathTyp ...

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

    前言: 前两篇博客介绍了 Python 的 docx 模块对 Word 文档的写操作,这篇博客将介绍如何用 docx 模块读取已有 Word 文档中的信息. 本篇博客主要内容有: 1.获取文档的章节信 ...

  9. C#/VB.NET 如何在 Word 文档中添加页眉和页脚

    页眉位于文档中每个页面的顶部区域,常用于显示文档的附加信息,可以插入时间.图形.公司微标.文档标题.文件名或作者姓名等:页脚位于文档中每个页面的底部的区域,常用于显示文档的附加信息,可以在页脚中插入文 ...

  10. C# 在Word文档中生成条形码

    C# 在Word文档中生成条形码 简介 条形码是由多个不同的空白和黑条按照一定的顺序组成,用于表示各种信息如产品名称.制造商.类别.价格等.目前,条形码在我们的日常生活中有着很广泛的应用,不管是在图书 ...

随机推荐

  1. quarkus依赖注入之一:创建bean

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于依赖注入 对一名java程序员来说,依赖注入应该是 ...

  2. Angular:error TS2717: Subsequent property declarations must have the same type. Property 'contentRect' mu st be of type 'DOMRectReadOnly', but here has type 'DOMRectReadOnly'.

    解决方案 在tsconfig.json的compilerOptions选项中添加如下内容"skipLibCheck": true. 如下图所示 之后重新启动项目. 如下图启动成功

  3. NAT与NAT实验

    1.NAT与NAT实验 NAT(网络地址翻译) 一个数据包目的ip或者源ip为私网地址, 运营商的设备无法转发数据. 实际场景问题 如下图 201.0.0.1 公网地址?   买的 运营商给你的​19 ...

  4. loader的三种配置方式

    在这篇 webpack处理css资源 文章中使用几个常用的loader 来编译 css 代码. 但其实 loader 的配置方式不止一种,一起来看看其它方式~ 在 webpack.config.js ...

  5. Web攻防--xxe实体注入

    web攻防--xxe实体注入 漏洞简介 XML 外部实体注入(也称为 XXE)是一种 Web 安全漏洞,允许攻击者干扰应用程序对 XML 数据的处理.它通常允许攻击者查看应用程序服务器文件系统上的文件 ...

  6. 【LaTeX】制作 PPT(更新中)

    目录 Beamer 模板 特性 frame 与 slide \pause itemize 中的尖括号 <strat-end> 参考资料 Beamer 模板 PPT 推荐用 Beamer 模 ...

  7. Go Web项目结构 + 基础代码

    Go Web工程 下面是项目的包图,可以通过包图来理清项目包的结构. Go Web工程 下面是项目的包图,可以通过包图来理清项目包的结构. 因为我是从Java转过来的,其实这种包的结构与Java的类似 ...

  8. OCI云主机环境如何上传下载文件

    OCI云主机的连接是使用密钥而非用户密码连接. 之前使用的非主流的一个SSH工具,正常连接主机都没问题,但需要手工输入用户. 可是在选择SFTP时,始终找不到用户名的设置,导致密钥连接的SFTP始终失 ...

  9. Towards Network Anomaly Detection Using Graph Embedding笔记

    Towards Network Anomaly Detection Using Graph Embedding 目录 Towards Network Anomaly Detection Using G ...

  10. js合并对象常用方法

    const person = { name: 'David Walsh', gender: 'Male' }; const tools = { computer: 'Mac', editor: 'At ...