Java后台生成ECharts图片

news2024/11/16 3:47:52

前言

 通过echarts的jar包,Java后台生成一张图片,并把图片插入到word中。关于word插图片的代码在下一章。

 需要用到的工具PhantomJS,Echarts-convert.js,jquery.js,echarts.js

1.PhantomJS 介绍

PhantomJS是一个不需要浏览器的富客户端。 

官方介绍:PhantomJS是一个基于 WebKit 的服务器端JavaScript API。它全面支持web而不需浏览器支持,支持各种Web标准:DOM处理,CSS选择器, JSON,Canvas,和SVG。
PhantomJS常用于页面自动化,网络监测,网页截屏,以及无界面测试等。

通常我们使用PhantomJS作为爬虫工具。传统的爬虫只能单纯地爬取html的代码,对于js渲染的页面,就无法爬取,如Echarts统计图。而PhantomJS正可以解决此类问题。

 我们可以这么理解PhantomJS,PhantomJS是一个无界面、可运行脚本的谷歌浏览器。

1.1 PhantomJS下载安装

PhantomJS安装非常简单,官网http://phantomjs.org/download.html下载最新的安装包, 安装包有Windows,Mac OS X, Linux 64/32 bit,选择对应的版本下载解压即可使用,在下载包里有个example文件夹,里面对应了许多示例供参考。

为方便使用,我们将phantomjs添加至环境变量中。

windows下操作:

path 下添加  C:\Users\ming\Desktop\java-echarts\phantomjs-2.1.1-windows\bin
打开cmd,查看是否配置成功
C:\Users\ming>phantomjs --version
2.1.1

linux 下操作:

export PHANTOMJS=/usr/local/phantomjs/phantomjs-2.1.1-linux-x86_64
export PATH=$PATH:$PHANTOMJS/bin
# or
export PATH=$PATH:/usr/local/phantomjs/phantomjs-2.1.1-linux-x86_64/bin
# 刷新配置
source /etc/profile

注:linux虽然不需要其他的依赖包,但仍旧需要GLIBCXX_3.4.9和GLIBC_2.7,当然大多数linux是有这两个依赖包的。

 1.2 运行脚本测试

 安装包下 example 文件夹下提供了好多示例,这里以hello.js 为例,在example文件夹下输入 phantomjs hello.js 命令 会输出 Hello, world! 更多用法可参考官方文档。

C:\Users\ming\Desktop\java-echarts\phantomjs-2.1.1-windows\examples>phantomjs hello.js
Hello, world!

 2.Echartsconvert

上面讲述了PhantomJS如何使用,下面我们就从Echarts官网使用JS截图的方式来获取我们想要的图片 

Echartsconvert (Gitee)copy下来代码
注意:因为该源码长期没有更新,script目录下echarts.min.js太过于老旧,无法支持目前Echarts的图形,请大家copy下代码后,更新替换其文件 。最新echarts.min.js下载传送门 

其中echarts-convert.js就是我们要使用到的主C,这个Js就相当于帮我们去Echarts官方运行Dome->生成折线图/柱状图->保存到指定文件夹下

jquery.js自行下载对应版本,我的是3.6.3

附上echarts-converts.js文件


;(function (window, document, undefined) {
    "use strict";

    // 引入module
    var system = require('system'), // 获取参数
        path = phantom.libraryPath,
        command = require(path + '/module/command.js');// 参数module

    /**
     * phantomJs 全局异常监听
     * @param msg
     * @param trace
     */
    phantom.onError = function (msg, trace) {
        var msgStack = ['Convert ERROR: ' + msg];
        if (trace && trace.length) {
            msgStack.push('TRACE:');
            trace.forEach(function (t) {
                msgStack.push(' -> ' + (t.file || t.sourceURL) + ': ' + t.line + (t.function ? ' (in function ' + t.function + ')' : ''));
            });
        }
        console.error(msgStack.join('\n'));
        phantom.exit(1);
    };

    /**
     * 参数
     * @type {Command}
     */
    var commandParams = command
        .version('0.0.1')
        .option('-s, --server', 'provide echarts convert http server')
        .option('-p, --port <number>', 'change server port when add -s or --server', 9090)
        .option('-o, --opt <json>', 'add the param of echarts method [ eChart.setOption(opt) ]')
        .option('-t, --type <value>', 'provide file/base64 for image, default file', /^(file|base64)$/i, 'base64')
        .option('-f, --outfile <path>', 'add output of the image file path')
        .option('-w, --width <number>', 'change image width', '600')
        .option('-h, --height <number>', 'change image height', '400')
        .parse(system.args);


    // ***********************************
    // Echarts转换器
    // ***********************************
    function Convert(params) {
        this.params = params || commandParams; // 参数命令
        this.external = {
            JQUERY3: path + '/script/jquery-3.6.3.min.js',
            ECHARTS: path + '/script/echarts.min.js',
            ECHARTS_CHINA: path + '/script/china.js'
        }; // 外部js
    }

    /**
     * 初始化
     */
    Convert.prototype.init = function () {
        var params = this.params;
        this.check(params);
        if (params.server) {
            this.server(params);
        } else {
            this.client(params);
        }
    };

    /**
     * 参数检查
     * @param params
     */
    Convert.prototype.check = function (params) {
        if (undefined === params.server && undefined === params.opt) {
            this.error("option argument missing -o, --opt <json>");
        }

        if (undefined !== params.opt) {
            var isJson = this.checkJson(params.opt);
            if (!isJson) {
                this.error("--opt <json> args not json string");
            }
        }

        if ('file' === params.type && undefined === params.outfile) {
            this.createTmpDir();
        }

    };

    /**
     * 检查是否是json字符串
     * @param value
     * @returns {boolean}
     */
    Convert.prototype.checkJson = function (value) {
        var re = /^\{[\s\S]*\}$|^\[[\s\S]*\]$/;
        // 类型为string
        if (typeof value !== 'string') {
            return false;
        }
        // 正则验证
        if (!re.test(value)) {
            return false;
        }
        // 是否能解析
        try {
            value = "\"" + value + "\"";
            JSON.parse(value);
        } catch (err) {
            return false;
        }
        return true;
    };

    /**
     * 创建临时目录,并指定输出路径
     */
    Convert.prototype.createTmpDir = function () {
        var fs = require('fs'); // 文件操作
        var tmpDir = fs.workingDirectory + '/tmp';
        // 临时目录是否存在且可写
        if (!fs.exists(tmpDir)) {
            if (!fs.makeDirectory(tmpDir)) {
                this.error('Cannot make ' + tmpDir + ' directory\n');
            }
        }
        this.params.outfile = tmpDir + "/" + new Date().getTime() + ".png";
    };

    /**
     * 服务
     * @param params
     */
    Convert.prototype.server = function (params) {
        var server = require('webserver').create(), // 服务端
            convert = this;

        var listen = server.listen(params.port, function (request, response) {
            /**
             * 输出
             * @param data
             * @param success
             */
            function write(data, success, msg) {
                response.statusCode = 200;
                response.headers = {
                    'Cache': 'no-cache',
                    'Content-Type': 'application/json;charset=utf-8'
                };
                response.write(convert.serverResult(data, success, msg));
                response.close();
            }

            //获取参数
            var args = convert.serverGetArgs(request);

            if (args.opt !== undefined) {
                var check = convert.serverCheckAndSet(params, args);

                if (check) {
                    convert.client(params, write);
                } else {
                    write("", false, "failed to get image, please check parameter [opt] is a JSON");
                }
            } else {
                write("", false, "failed to get image, missing parameter [opt]");
            }

        });

        // 判断服务是否启动成功
        if (!listen) {
            this.error("could not create echarts-convert server listening on port " + params.port);
        } else {
            console.log("echarts-convert server start success. [pid]=" + system.pid);
        }
    };

    /**
     * 服务参数检查和赋值
     * @param params
     * @param args
     * @returns {boolean}
     */
    Convert.prototype.serverCheckAndSet = function (params, args) {
        if (this.checkJson(args.opt)) {
            params.opt = args.opt;
        } else {
            return false;
        }

        if (/^(file|base64)$/i.exec(args.type)) {
            params.type = args.type;
        }

        if (!isNaN(args.width)) {
            params.width = args.width;
        }

        if (!isNaN(args.height)) {
            params.height = args.height;
        }
        return true;
    };

    /**
     * 结果返回
     * @param data
     * @param success
     * @param msg
     */
    Convert.prototype.serverResult = function (data, success, msg) {
        var result = {
            code: success ? 1 : 0,
            msg: undefined === msg ? success ? "success" : "failure" : msg,
            data: data
        };

        return JSON.stringify(result);
    };

    /**
     * 获取参数
     * @param request
     * @returns {{}}
     */
    Convert.prototype.serverGetArgs = function (request) {
        var args = {};
        if ('GET' === request.method) {
            var index = request.url.indexOf('?');
            if (index !== -1) {
                var getQuery = request.url.substr(index + 1);
                args = this.serverParseArgs(getQuery);
            }
        } else if ('POST' === request.method) {
            var postQuery = request.post;
            args = this.serverParseArgs(postQuery);
        }
        return args;
    };

    /**
     * 解析参数
     * @param query 字符串
     * @returns {{}} 对象
     */
    Convert.prototype.serverParseArgs = function (query) {
        var args = {},
            pairs = query.split("&");
        for (var i = 0; i < pairs.length; i++) {
            var pos = pairs[i].indexOf('=');
            if (pos === -1)
                continue;
            var key = pairs[i].substring(0, pos);
            var value = pairs[i].substring(pos + 1);
            // 中文解码,必须写两层
            value = decodeURIComponent(decodeURIComponent(value));
            args[key] = value;
        }
        return args;
    };

    /**
     * 访问渲染
     * @param params
     * @param fn
     */
    Convert.prototype.client = function (params, fn) {
        var page = require('webpage').create(); // 客户端
        var convert = this,
            external = this.external,
            render,
            output;

        /**
         *  渲染
         * @returns {*}
         */
        render = function () {
            switch (params.type) {
                case 'file':
                    // 渲染图片
                    page.render(params.outfile);
                    return params.outfile;
                case 'base64':
                default:
                    var base64 = page.renderBase64('PNG');
                    return base64;

            }
        };

        /**
         * 输出
         * @param content 内容
         * @param success 是否成功
         */
        output = function (content, success, msg) {
            if (params.server) {
                fn(content, success, msg);
                page.close();
            } else {
                console.log(success ? "[SUCCESS]:" : "[ERROR]:" + content);
                page.close();
                convert.exit(params);// exit
            }
        };

        /**
         * 页面console监听
         * @param msg
         * @param lineNum
         * @param sourceId
         */
        page.onConsoleMessage = function (msg, lineNum, sourceId) {
            console.log(msg);
        };

        /**
         * 页面错误监听
         * @param msg
         * @param trace
         */
        page.onError = function (msg, trace) {
            output("", false, msg); // 失败,返回错误信息
        };

        // 空白页
        page.open("about:blank", function (status) {
            // 注入依赖js包
            var hasJquery = page.injectJs(external.JQUERY3);
            var hasEchart = page.injectJs(external.ECHARTS);
            var hasEchartChina = page.injectJs(external.ECHARTS_CHINA);

            // 检查js是否引用成功
            if (!hasJquery && !hasEchart) {
                output("Could not found " + external.JQUERY3 + " or " + external.ECHARTS, false);
            }

            // 创建echarts
            page.evaluate(createEchartsDom, params);

            // 定义剪切范围,如果定义则截取全屏
            page.clipRect = {
                top: 0,
                left: 0,
                width: params.width,
                height: params.height
            };

            // 渲染
            var result = render();
            // 成功输出,返回图片或其他信息
            output(result, true);
        });
    };

    /**
     * 创建eCharts Dom层
     * @param params 参数
     */
    function createEchartsDom(params) {
        // 动态加载js,获取options数据
        $('<script>')
            .attr('type', 'text/javascript')
            .html('var options = ' + params.opt)
            .appendTo(document.head);

        // 取消动画,否则生成图片过快,会出现无数据
        if (options !== undefined) {
            options.animation = false;
        }

        // body背景设置为白色
        $(document.body).css('backgroundColor', 'white');
        // echarts容器
        var container = $("<div>")
            .attr('id', 'container')
            .css({
                width: params.width,
                height: params.height
            }).appendTo(document.body);

        var eChart = echarts.init(container[0]);
        eChart.setOption(options);
    }

    /**
     * debug,将对象转成json对象
     * @param obj
     */
    Convert.prototype.debug = function (obj) {
        console.log(JSON.stringify(obj, null, 4));
    };

    /**
     * 错误信息打印并退出
     * @param str 错误信息
     */
    Convert.prototype.error = function (str) {
        console.error("Error:" + str);
        this.exit();
    };

    /**
     * 退出,参数为空或是server时,不退出
     * @param params 参数
     */
    Convert.prototype.exit = function (params) {
        if (undefined === params || undefined === params.server) {
            phantom.exit();
        }
    };

    // 构建,入口
    new Convert(commandParams).init();

}(this, this.document));

 结构如下:

 2.1 启动命令

windows下启动命令 端口默认 9090

C:\Users\ming\Desktop\java-echarts\echartsconvert>phantomjs echarts-convert.js -s

 linux 下启动命令 端口默认 9090

nohup phantomjs /usr/local/phantomjs/echartsconvert/echarts-convert.js -s &

 2.2 中文字体乱码问题

 linux 下 phantonjs 没有支持的中文, 需要进行安装对应包。也可安装其它字体库,这里不做更多叙述。

此处执行linux字符集安装即可
在centos中执行:yum install bitmap-fonts bitmap-fonts-cjk
在ubuntu中执行:sudo apt-get install xfonts-wqy

 2.3 饼图无法生成,Null异常等问题

饼图绘制不了,request时就卡住的问题
solution1:此处并不是饼图绘制不了,而是只要opt中含有'%'都会挂,原因是作者在代码里执行了两次decodeURIComponent(详情参考echarts-convert.js源码259行),所以'%'传递时也必需encode两次,否则会造成%后的json串无法被decode导致卡住的问题。
此处可以将'%'替换为'%25'解决,或是改源码将decodeURIComponent改为一次,暂时没有发现改为一次decode会出现中文问题

3.JSON格式数据生成图表图片

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 以 EchartsData 示例中 getBar() 数据为例,将生成的Echarts数据转为base64图片
 *
 * @author demain_lee
 * @since  2022/12/28
 */
public class EchartData2Base64 {


    public static void main(String[] args) throws IOException {

      	// json 格式数据
        String jsonData = "{"xAxis":[{"type":"category","data":["Matcha Latte","Milk Tea","Cheese Cocoa","Walnut Brownie"]}],"yAxis":[{"type":"value"}],"tooltip":{"trigger":"item"},"legend":{},"series":[{"type":"bar","name":"2020","data":[43.3,83.1,86.4,72.4]},{"type":"bar","name":"2021","data":[85.8,73.4,65.2,53.9]},{"type":"bar","name":"2022","data":[93.7,55.1,82.5,39.1]}]}";

        // PhantomJS 服务器地址
        String url = "http://localhost:9090";
        Map<String, String> map = new HashMap<>();
        //此处已将%处理为%25
        jsonData = jsonData.replaceAll("\\s+", "").replaceAll("\"", "'").replaceAll("%", "%25");
        map.put("opt", jsonData);
        String resultData = post(url, map, "utf-8");
        System.out.println(resultData);

    }

    public static String post(String url, Map<String, String> map, String encoding) throws IOException {
        String body = "";

        // 创建httpclient对象
        CloseableHttpClient client = HttpClients.createDefault();
        // 创建post方式请求对象
        HttpPost httpPost = new HttpPost(url);

        // 装填参数
        List<NameValuePair> nvp = new ArrayList<>();
        if (map != null) {
            for (Map.Entry<String, String> entry : map.entrySet()) {
                nvp.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
        }

        // 设置参数到请求对象中
        httpPost.setEntity(new UrlEncodedFormEntity(nvp, encoding));

        // 执行请求操作,并拿到结果(同步阻塞)
        CloseableHttpResponse response = client.execute(httpPost);
        // 获取结果实体
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            // 按指定编码转换结果实体为String类型
            body = EntityUtils.toString(entity, encoding);
        }
        EntityUtils.consume(entity);
        // naps
        response.close();
        return body;
    }
}

3.1 base64格式数据

resultData 返回的数据包含base64格式数据 

//返回数据
iVBORw0KGgoAAAANSUhEUgAAAyAAAAGQCAYAAABWJQQ0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAgAElEQVR4nO3deXhU5d3/8U9mkpnsGxCysAQSQEAJZEFABGSRRZBNqWttRa2o1VprtS60j23x0fbpr9bWiqJ1qyIKCiqrKAIChk1AQZAQ1myEhCRkmUkm8/sjOBJJQpDkngTer+viupj7nHPne2buOTOfs42P2+12CwAAAAAMsHi7AAAAAAAXDgIIAAAAAGMIIAAAAACMIYAAAAAAMIYAAgAAAMAYAggAAAAAYwggAAAAAIwhgAAAAAAwhgACAAAAwBgCCAAAAABjCCAAAAAAjCGAAAAAADCGAAIAAADAGF9vFwAATeVIXrlee/+Adu0rVrnD1SR9xrTz18iB7TV+aIwsFp8m6dM0t7taW/PW6Kv8dB2vONokfdqsdsUGd9HlHcYrwj+qSfr0Bnd5llwHX5e7ZLfkKm+SPn38Y2SJGi5L9DjJh/18APBDPm632+3tIgDgXBUUOfXrp7aptLyqWfqfODxWN03o3Cx9N7e1hz/SltzPmqVvuzVAN/b+tYL9wpql/+bkdhaoavuDkqusWfq3xEyQtdMNzdI3ALRm7JoBcF746LPsZgsfkrTo0yyVV9R9VGXevHm64YYbNHjwYI0fP15z5szRqft2Dhw4oFtvvVWDBg3StGnTtGXLFs+09PR0zZgxQ8OGDdPIkSP1+9//XsXFxZ7pZWVlevTRRzV48GCNGTNG8+bNO6u6na4Kbc1dfZZr23gOV7m+zF1b57SsrCz97ne/0+jRozVkyBDNmDFDGRkZteaZM2eORo0apSFDhmjWrFmqqqp5DV0ul55++mlNnDhRl112ma677jp9+umnnuXy8/N1//3368orr1Rqaqry8vLOuvbqnCXNFj4kqTr7w3qPqjTXmDnT8wYALQEBBMB5YX9WabP273ZLB7Pr/rKan5+ve++9V4sWLdITTzyht99+Wx988IEkqbq6Wg8++KAuueQSrVixQtOmTdMDDzygkpISSVJOTo4mTJiguXPnas6cOTp06JD++te/evp+5plndPToUX3wwQd66qmn9M9//lObN29udN3HynPlVvMe6M4vz6q7PT9fPXr00OzZs7VgwQJ17NhR9913n+eL9vLlyzVv3jw999xzevfdd7V9+3a9/PLLkqSqqiq53W49+eST+uCDD3TdddfpkUce0YEDByRJFotFgwcP1qxZs3503e6yAz962Ub+BbnLDtU5pbnGzJmeNwBoCQggAM4LVVXNfzZplavuv3HXXXdpwIABioyMVHJyskaMGKEdO3ZIknbt2qXDhw/rzjvvVFBQkK655hpFRkZq1apVkqSrr75a48aNU3R0tOLj43X99dfrq6++klTzRXTJkiW6/fbbFRERoaSkJI0ZM8bzRbUxXO7mOyr0/d+o+8hQnz599LOf/Uzx8fFq27at7rzzTuXk5Cg/P1+S9OGHH2rKlCnq1q2boqKidOutt3rWzW6366GHHlKvXr0UGRmpSZMmqUOHDtq1a5ckKTIyUlOnTlWPHj1+fOEGnpv6/kZzjZkzPW8A0BIQQACgCbndbu3YsUOJiYmSpIyMDHXq1EkBAQGeeXr06KF9+/bVufypy+bk5KisrKzWl+yGlm3pduzYoYiICLVp00aStG/fPnXv3t0zvXv37srOzlZZ2elHmgoKCnTkyBHPc3M+acox80Pn8/MGoPXiLlgA0IRmz54tt9utyZMnS5LKy8sVFBRUa57g4GCVlp5+ytiGDRv04Ycf6j//+Y8keb6In7p8fcu2dLm5uXrqqaf0m9/8RhZLzb6vsrIyBQcHe+b57v/l5eUKDAz0tFdWVuqxxx7T+PHjz8sv0k05Zk51vj9vAFovjoAAQBN54403tHz5cj377LOy2WySpICAgNO+OJ44ceK0L5hbtmzR448/rr/97W/q3LnmblvffQk/dfm6lm3p8vPzNWPGDN14440aPXq0pz0wMFAnTpzwPP7u/6fu+Xe5XHrkkUcUFBSkhx56yFzRhjT1mPnO+f68AWjdCCAA0ATmzp2refPm6fnnn/ecYiRJCQkJOnTokBwOh6dtz5496tq1q+fx9u3b9dBDD+nPf/6z+vbt62mPjo5WYGCgvv3223qXbekKCgo0Y8YMjR8/XjfeeGOtaV27dtXevXs9j/fs2aOYmBhP8Kqurtajjz4qp9OpWbNmyWq1Gq29uTXHmJHO/+cNQOtHAAGAczR//ny98sor+sc//qHw8HA5nU7P7WR79uypuLg4vfDCC6qoqNDChQt17NgxDR06VJK0c+dO/epXv9Lvfvc79e3bV06nU06nU1LNnZ7GjBmjOXPmqLi4WDt37tSyZcs0YcIEr63r2SgqKtKMGTM0ePBg3XTTTZ51++4uWOPHj9eCBQuUmZmpY8eO6dVXX/WsW3V1tWbOnKmCggL96U9/ktvtltPpVHV1tad/p9OpyspKz/+/e95ag+YaM4153gDA2/ghQgDnhT8+v1Pbdxc169/4070Xq0eXkNPaJ02apMOHD9dqGzx4sP7+979Lkvbv368nnnhC33zzjTp06KCHH35YycnJNXX/8Y9auHBhrWUDAgK0Zs0aSTXXSfzpT3/S6tWrFRQUpOnTp2vatGmNrjn7xAG9s/tfZ7WeZ6tTaDdN6nb7ae2LFy/WzJkzT2t/7bXX1KtXL0nSiy++qLlz56qyslJjxozRb3/7W/n6+iovL0/jxo07bdn77rtPN998syQpNTX1tOmrV6+udf1IQ6q+mSV30Y5Gzftj+fb6H/mEdD+tvbnGTGOeNwDwNgIIgPPC64sOaNGndf8eRVPw8ZFendVfAf6t63QWp6tCs7/8fbP+Fkhy+6Ea3OGqZuu/ubgO/rfmxwKbjY/8Ul+SrAFnnhUALiCcggXgvHDV0BgFBTTfjf2uviK21YUPSbJZ/dWv/ZBm699uDVDf9oObrf/mZIkeK1kbd7TkR/UfM57wAQB14AgIgPPGkbxyvfb+Ae3KLFZ5Rd0/jne2YqMCNGJAlMYPjZHF4tMkfZrmdldra94afZ2frsKKo03Sp83qr9jgeF3eYYIi/Ns1SZ/e4C7Pkuvg63KX7JZc5U3Sp49/rCxRV8gSPU7yYT8fAPwQAQQAAACAMeyaAQAAAGAMAQQAAACAMQQQAAAAAMYQQAAAAAAYQwABAAAAYAwBBAAAAIAxBBAAAAAAxhBAAAAAABhDAAEAAABgDAEEAAAAgDEEEAAAAADGEEAAAAAAGEMAAQAAAGAMAQQAAACAMQQQAAAAAMYQQAAAAAAYQwABAAAAYAwBBAAAAIAxBBAAAAAAxhBAAAAAABhDAAEAAABgDAEEAAAAgDEEEAAAAADGEEAAAAAAGEMAAQAAAGAMAQQAAACAMQQQAAAAAMYQQAAAAAAYQwABAAAAYAwBBAAAAIAxBBAAAAAAxhBAAAAAABhDAAEAAABgDAEEAAAAgDG+3i7AlDVr1mvDF5uVlZ2j/v2Tdf1PptSanpeXrzfnvqtDh7LUtm0bTbvmaiUkdPFMX7b8E61es16VlVVKTUnS1CkTZLVaTa8GAAAA0KpdMEdAQsNCNXbMCKWkJJ02ze1266X/vKH4zp305z8+qssHD9CLL72u8vIKSdLWrdu1Zu0G3T1juh55+FfKzDyo5StWGV4DAAAAoPW7YAJIUp/euvjingoMCDht2sFDR3Qsv0Djxo6Uv79dgy+7VCEhwdq+Y6ckKX3jFg0a2F+xsdEKDw/TqFHDlJ6+2fQqAAAAAK3eBRNAGpKTk6t27drKZrN52uLiYpWTkytJys7JU1xczCnTYlRQeFwOh9N4rQAAAEBrRgCR5HA45e9vr9UW4O8vh8Nxcrqj1vQAf/+adqfDXJEAAADAeeCCuQi9IXa7TRUVtcNEeUWFIsLDTk6315peXlFzbYjdVju01OVAdnETVgoAAICWrnNMqLdLaNEIIJKio9vraP4xVVZWys/PT5KUdSRbPS/qLkmKiY5SVlaOkvr09kyLjAiX3W6rt8/vMAABAACA710wp2BVV1ersqpK1W633N/9v7paktSpY5zatInQ0mWfyOms1IYNm1RcUqJLLu4pSUpLS9a69enKzc1TcUmJPl65Wv37p3hzdQAAAIBWycftdru9XYQJS5au1NJlK2u1XTFssCZNHCdJys07qjffmq/Dh7PUtm2kpl0zsdbvgCxd9olWr1mnqiqXUpKTdM1UfgcEAAAAOFsXTAABAAAA4H0XzClYAAAAALyPAAIAAADAGAIIAAAAAGMIIAAAAACMIYAAAAAAMIYAAgAAAMAYAggAAAAAYwggAAAAAIwhgAAAAAAwhgACAAAAwBgCCAAAAABjCCAAAAAAjPH1dgGAt7ky/q3qY2u9XYaHJWqErPG3ersMAACAZkEAAVQtuau9XcT3WlItAAAATYxTsAAAAAAYQwABAAAAYAwBBAAAAIAxBBAAAAAAxhBAAAAAABhDAAEAAABgDAEEAAAAgDEEEAAAAADGEEAAAAAAGEMAAQAAAGAMAQQAAACAMQQQAAAAAMYQQAAAAAAYQwABAAAAYAwBBAAAAIAxBBAAAAAAxhBAAAAAABjj6+0CAABoyOGccuUfd3i7DI+IUJs6xwZ6uwwAaLUIIACAFm3xmmytWJfr7TI8Lk9pq3tv6ubtMgCg1SKAoMmVV5Xqs4Pve7uMWoZ2mqQA3yBvlwEAAHDBI4CgyVVWO7WncJu3y6hlUIdxChABBAAAwNu4CB0AAACAMQQQAAAAAMYQQAAAAAAYQwABAAAAYAwBBAAAAIAx3AULwBnt2V+irLwKb5fh0b6tXT27hnq7DAAA8CMQQACc0aqNR1vcD8ERQAAAaJ0IIAAAXKCqj7yn6uId3i7DwxKeIkvMVd4uA0AzI4AAAHCBcldkyV28y9tleLj9Y71dAgADCCAnff31N/rwo+U6mn9MYWEhunLUcF3aP9kzfdnyT7R6zXpVVlYpNSVJU6dMkNVq9WLFAAAAQOtDAJFUVlaul195U1OnjNel/VO0d2+mXpjzmjp3ilN0dHtt3bpda9Zu0N0zpiswMECzX3hVy1es0tgxI7xdOgAAANCqcBteScXFxXK5XBpwaaqsVqt69EhUm8gI5eQelSSlb9yiQQP7KzY2WuHhYRo1apjS0zd7uWoAAACg9SGASGrfPko9eiRqwxeb5HK5tHvPXp0oLVPXLp0lSdk5eYqLi/HMHxcXo4LC43I4nN4qGQAAAGiVOAVLko+Pj/qnJeuddxbq7Xnvy2q16vrrpig0NESS5HA45O9v98wf4O9f0+50yG63eaVmAAAAoDUigEg6dOiI3nxrvqbfepN6dE9QVlaOXnjxNYWFhqp79wTZ7XZVVDg885dX1Pwgm91mr69LjwPZxc1Wd0tV5jrh7RJOcyTvhArruWlARHmlAgzX05CSMqeKWti4KSlrWUf7SssrL8j31oWK8dd82P4BzaNzDL9V1RACiKSs7Fy1j2qnXj27S5I6doxTQmIX7d6zV927JygmOkpZWTlK6tO7Zv4j2YqMCG/U0Y8LcQAWO11Sy/nNOklSXFSwQm11vxauMj9VlxouqAEhgTaFt7BxExKY7+0SagkK8Lsg31sXKsZf82H7B8AbuAZEUqeOcco7mq/du/fK7XbrSFa29n67T7Gx0ZKktLRkrVufrtzcPBWXlOjjlavVv3+Kl6sGAAAAWh+OgEiKiWmv66ZN1vwFH6jw+HEFBgZq0KD+Su7XR5KU3K+P8vLy9cyzL6iqyqWU5CRdOWqYd4sGAAAAWiECyEmpqX2Vmtq33uljRg/XmNHDDVYEAAAAnH84BQsAAACAMQQQAAAAAMYQQAAAAAAYQwABAAAAYAwBBAAAAIAxBBAAAAAAxhBAAAAAABhDAAEAAABgDAEEAAAAgDEEEAAAAADGEEAAAAAAGEMAAQAAAGCMr7cLAICm5HK7VFCe6+0yaokMaC+rj9XbZQAA0CIQQACcV0ori/XWrr97u4xafnbJ7xRqi/B2GQAAtAicggUAAADAGI6AAAAA1GPrruPad+iEt8vw6BQbqLSLI71dBnBOCCAAAAD12PhVgVasaznXlV2e0pYAglaPU7AAAAAAGEMAAQAAAGAMAQQAAACAMQQQAAAAAMYQQAAAAAAYQwABAAAAYAwBBAAAAIAxBBAAAAAAxhBAAAAAABhDAAEAAABgDAEEAAAAgDEEEAAAAADGEEAAAAAAGEMAAQAAAGAMAQQAAACAMQQQAAAAAMYQQAAAAAAY4+vtAgAAOH+4VVld6e0iavGz+Eny8XYZAOBBAAEAoIkUO4/rlR1PeruMWn52ye8UaovwdhkA4MEpWAAAAACMIYAAAAAAMIYAAgAAAMAYAggAAAAAYwggAAAAAIwhgAAAAAAwhgACAAAAwBgCCAAAAABj+CHCk6qrq7V4ycf64ovNKi8vV0xstO656zbZ7TZJ0rLln2j1mvWqrKxSakqSpk6ZIKvV6uWqAQAAgNaFAHLSR4tXaM+evbr9tpsVERmurKwcWaw1B4i2bt2uNWs36O4Z0xUYGKDZL7yq5StWaeyYEV6uGgAAAGhdOAVLUmlpmVavWaef3nydOnXqoJDgYPXonig/35p8lr5xiwYN7K/Y2GiFh4dp1KhhSk/f7OWqAQAAgNaHIyCSjmRlKzAwUOs3bNS69ekKCgrUFcMGa/BlAyRJ2Tl5GjAgzTN/XFyMCgqPy+Fwek7RAgAAAHBmBBBJRceLVVRULIfDqSf+8LCysnP13L9fVru2bdWjR6IcDof8/e2e+QP8/SVJDqeDAAIAAACcBQKIJD8/X7ndbo0bO1I2m03xnTuqb1Jv7dy1Wz16JMput6uiwuGZv7yiQpJkt9nr69LjQHZxs9XdUpW5Tni7hNMcyTuhwnpuGhBRXqkAw/U0pKTMqaIWNm5KypzeLqGW0vLKet9brW384cwYf+eG7d+5aU3jDy1H55hQb5fQohFAJMXGxjQ4PSY6SllZOUrq01uSlHUkW5ER4Y06+nEhDsBip0vK9XYVtcVFBSvUVvdr4SrzU3Wp4YIaEBJoU3gLGzchgfneLqGWoAC/et9brW384cwYf+eG7d+5aU3jD2gtuAhdUlRUW8XHd9LSZStVWVWlg4eOaNv2r9W710WSpLS0ZK1bn67c3DwVl5To45Wr1b9/iperBgAAAFofjoCc9PNbrtebc+frd4/8UaGhwbp6wlh1754gSUru10d5efl65tkXVFXlUkpykq4cNcy7BQMAAACtEAHkpPDwMN115631Th8zerjGjB5usCIAAADg/MMpWAAAAACMIYAAAAAAMIYAAgAAAMAYAggAAAAAYwggAAAAAIwhgAAAAAAwhgACAAAAwBgCCAAAAABj+CFCAPCi6iMLVF203dtleFgiUmWJGe/tMgAA5zECCAB4kbsiW+6S3d4uw8Md0MHbJQAAznOcggUAAADAGI6AAAAAnAccrnJtyvnU22XUkhp9hezWAG+XgRaGAAIAAHAecLgqtDlnlbfLqOWSdgMJIDgNp2ABAAAAMIYAAgAAAMAYAggAAAAAYwggAAAAAIwhgAAAAAAwhgACAAAAwBgCCAAAAABjCCAAAAAAjCGAAAAAADCGAAIAAADAGAIIAAAAAGMIIAAAAACMIYAAAAAAMIYAAgAAAMAYAggAAAAAYwggAAAAAIwhgAAAAAAwhgACAAAAwBgCCAAAAABjCCAAAAAAjCGAAAAAADCGAAIAAADAGAIIAAAAAGMIIAAAAACMIYAAAAAAMIYAAgAAAMAYAggAAAAAY3y9XQAa540PDmjVxqPeLsNjYFIbTZ/axdtlAAAAoJUhgLQSZRUuFZVUersMj9LyKm+XAAAAgFaIU7AAAAAAGEMAAQAAAGAMp2D9wLZtX+nlV97UuLGjNPrKKzzty5Z/otVr1quyskqpKUmaOmWCrFarFysFAAAAWh+OgJzC4XDqo8UrFBcXU6t969btWrN2g+6eMV2PPPwrZWYe1PIVq7xTJAAAANCKEUBOsWTpx7r00hSFh4XWak/fuEWDBvZXbGy0wsPDNGrUMKWnb/ZSlQAAAEDrRQA5KSs7R9/s/lbDhl522rTsnLxaR0Xi4mJUUHhcDofTZIkAAABAq0cAkeR2u/XOu4s0edJVdV7X4XA45O9v9zwO8PevaXc6jNUIAAAAnA+4CF01p1iFhoaoR/fEOqfb7XZVVHwfNsorKmrabfY65z/VgeziJqmxpKxlHW0pLa+sd93KXCcMV3NmR/JOqLCemwZElFcqwHA9DSkpc6qoicZNU2H8nRvG37lh/J0bxt+5Yfydm4bG3/msc0zomWe6gBFAJO3Zk6Hd33yrx2bOkiSVl1dob0amsrJz9PNbrldMdJSysnKU1Ke3JCnrSLYiI8Jlt9vO2HdTDcCQwPwm6aepBAX41btuxU6XlGu4oDOIiwpWqK3uel1lfqouNVxQA0ICbQpvYRsuxt+5YfydG8bfuWH8nRvG37lpaPzhwkUAkXTNNVdr4sSxnsdvvPGOOnaM04jhQyRJaWnJeu/9j5Tc7xIFBAbo45Wr1b9/irfKBQAAaPWq89fKXbrP22V4+IR0lyVygLfLuCAQQFRzTcd313VIkq+vVTabTYGBNQemk/v1UV5evp559gVVVbmUkpykK0cN81K1AAAArZ+7aJuq89d6uwwPS7VTIoAYQQCpwx2333Ja25jRwzVm9HAvVAMAAACcP7gLFgAAAABjCCAAAAAAjCGAAAAAADCGAAIAAADAGAIIAAAAAGMIIAAAAACMIYAAAAAAMIYAAgAAAMAYAggAAAAAYwggAAAAAIwhgAAAAAAwhgACAAAAwBgCCAAAAABjCCAAAAAAjCGAAAAAADCGAAIAAADAGAIIAAAAAGMIIAAAAACMIYAAAAAAMIYAAgAAAMAYAggAAAAAYwggAAAAAIwhgAAAAAAwhgACAAAAwBgCCAAAAABjCCAAAAAAjCGAAAAAADCGAAIAAADAGAIIAAAAAGMIIAAAAACMIYAAAAAAMIYAAgAAAMAYAggAAAAAYwggAAAAAIwhgAAAAAAwhgACAAAAwBgCCAAAAABjCCAAAAAAjCGAAAAAADCGAAIAAADAGAIIAAAAAGMIIAAAAACMIYAAAAAAMIYAAgAAAMAYX28X0BIcO1aoDz5cqoyM/XI4nercqYOmTB6vmJj2nnmWLf9Eq9esV2VllVJTkjR1ygRZrVYvVg0AAAC0PhwBkVRSUqIOHWJ1zz236bFHfq127dpo9ouvyu12S5K2bt2uNWs36O4Z0/XIw79SZuZBLV+xyrtFAwAAAK0QAURSfHwnjRwxVO2j2ik0NERjx45UYeFxFReXSJLSN27RoIH9FRsbrfDwMI0aNUzp6Zu9XDUAAADQ+hBA6rB//0EFBwcpNDREkpSdk6e4uBjP9Li4GBUUHpfD4fRWiQAAAECrRAD5gePHi/TOu4s0ZfJ4+fj4SJIcDof8/e2eeQL8/WvanQ6v1AgAAAC0VlyEfori4hL967mXdMWwwUpJTvK02+12VVR8HzbKKypq2m320/r4oQPZxU1SW0lZyzraUlpeWe+6lblOGK7mzI7knVBhPTcNiCivVIDhehpSUuZUURONm6bC+Ds3jL9zw/g7N4y/c8P4OzcX6vjrHBPaJP2crwggJ5WcOKF/PveS0tL66Yphg2tNi4mOUlZWjpL69JYkZR3JVmREuOx22xn7baoBGBKY3yT9NJWgAL96163Y6ZJyDRd0BnFRwQq11V2vq8xP1aWGC2pASKBN4S1sw8X4OzeMv3PD+Ds3jL9zw/g7N4w/1IVTsCSVlpbpX8+9pN69euiKKy5XZVWVKquqPHfBSktL1rr16crNzVNxSYk+Xrla/funeLlqAAAAoPXhCIikXbv2KDs7V9nZufrk0zWe9gd+fbc6dYxTcr8+ysvL1zPPvqCqKpdSkpN05ahh3isYAAAAaKUIIJJSU/sqNbVvg/OMGT1cY0YPN1QRAAAAcH7iFCwAAAAAxhBAAAAAABhDAAEAAABgDAEEAAAAgDEEEAAAAADGEEAAAAAAGEMAAQAAAGAMAQQAAACAMQQQAAAAAMYQQAAAAAAYQwABAAAAYAwBBAAAAIAxBBAAAAAAxhBAAAAAABhDAAEAAABgDAEEAAAAgDEEEAAAAADGEEAAAAAAGEMAAQAAAGAMAQQAAACAMQQQAAAAAMYQQAAAAAAYQwABAAAAYAwBBAAAAIAxBBAAAAAAxhBAAAAAABhDAAEAAABgDAEEAAAAgDEEEAAAAADGEEAAAAAAGEMAAQAAAGAMAQQAAACAMQQQAAAAAMYQQAAAAAAYQwABAAAAYAwBBAAAAIAxBBAAAAAAxhBAAAAAABhDAAEAAABgDAEEAAAAgDEEEAAAAADGEEAAAAAAGEMAAQAAAGAMAQQAAACAMb7eLqC1WLb8E61es16VlVVKTUnS1CkTZLVavV0WAAAA0KpwBKQRtm7drjVrN+juGdP1yMO/UmbmQS1fscrbZQEAAACtDgGkEdI3btGggf0VGxut8PAwjRo1TOnpm71dFgAAANDqEEAaITsnT3FxMZ7HcXExKig8LofD6cWqAAAAgNaHANIIDodD/v52z+MAf/+adqfDWyUBAAAArZKP2+12e7uIlu4PTzytyZOuUlKf3pKk3LyjmvXk/9PT//sH2e22epdbumyllixdaapMAAAAtBDP/L9Z3i6hxeIuWI0QEx2lrKwcTwDJOpKtyIjwBsOHJI0ZPUJLlq5kAMJr7rv/EcYfvIbxB29i/MGb7rv/EW+X0KJxClYjpKUla936dOXm5qm4pEQfr1yt/v1TvF0WAAAA0OpwBKQRkvv1UV5evp559gVVVbmUkpykK0cN83ZZAAAAQKtDAGmkMaOHa8zo4d4uAwAAAGjVuAgdAAAAgDFcAwIAAADAGAIIAAAAAGMIIAAAAACMIYAAAAAAMIYAAq9av2Gj5rz0hrfLwF6qDkAAABLISURBVAXkrbcX6OOVn0mSNm36Uv+e/R/PtJl/+F8dOZLtrdLQwrB9Qkv1xz//VfsPHPJ2GUaduu1G68dteOGxY8dOzXn5DaWl9tNNN17rad+7d5+e/dcc9e7VQ3fcfssZ+/nvm++qfft2GjliaHOWqx07dmrBex/q9zN/e1bLfZG+RVu2btOMX/zc02aqZjSfp//yrI5kZevxR3+jtm0jPe3P/vNF7c3I1AO/vludOsYpNaWvAvz9G93v7j179dy/X653+u8ff1CRkRHnVDu8JysrRx8tWaF9+/bL5XKpffsoXT54gPqnJXu7tDqVlpbqo8Uf66uvd6m0tEyRkeFKSU7S8Csul81m83Z5qMfKT9Zo955vddedt3raZj35/xQeEXZa25WjrlBqal8jddX1ebjgvQ/12ep1kiSLxaJ2bdto3NiR6tv3EiM11edst91o2QggqCUsLFTffPOtnE6n58MsfeNWRbVr6+XKgDOLatdWmzZv1ZjRIyRJBYXHdaygUDabn2eeboldz6rP7t0S9Ne/PCFJOpqXr6f+8g/975Mz5etbs/n0tVqbqHqYlpWdo7//43kNuDRVEyeMVXh4qI4cydHSZStbZABxOJx65tkXFRYWotum36z2Ue1UVFSsNWvX69ChI0pI6OLtElGPxIR4LV22UtXV1bJYLDpxolQOp1NZR3JqteXmHVVCQry3y1VaWj/dcN1UVVW5tOOrnXrtjXnq0qWzwsJCT5v3u/qb29luu9GyEUBQi91uU8cOcdq2/WulpfZTZWWldu7crUGD0nT4cJakmo3Nf155S/sya/YYdu7cUT+5dpIiIyO0afOX+nLbV7JaLfr88y/U46Juum7aZBUXl2jB+x9p77f75Kp2qXfvi3TTDTVHWard1Xp3/iKlb9yqkJAgXfeTKZ4NzdJlK/XFF5tVWlamtm3aaNKkcereLeGM61Hfcvn5BVq4aLGcDqf+54mn5e/vrxEjhtRZ8+HDWZr/3ofKzs5ReHiYJk+8Sj16JDbfk49zlpraV+kbvw8gGzduVUpyklavWe+Z5623F6hd2zZnPNp18NARvfTyG/rJtMnq1bO7JMnqWxM2fH195ef7/ebT5XJpydKV2rT5S1VWVurii3tq6uTxstlsys07qrfffk9Z2Tnytfrqkkt6acqU8bWWh3d8+OEy9ezZQ1Mmj/e0denSSXf+4meexw1tnxp63SVp85ZtWr7iUxUdL1bHTnG6btpktWlTc3Ru6bJP9Pm6L1TprFRwSJBuuuFaxcd3arDPz9d9IYfDoTtuv8czfqKi2mrqlAn67ie98vMLNO/d93Xw4GGFhARrzOgRSklOkiRVVVVp6bJPtHnLNpWWlik6Okq/uP0WBQUFavfuvVq4aImOFRQoOrq9rr3manWIi5Ukbd/xtZYsWaljBQUKCgzUsGGDNXTIoOZ9cc4zHTvGSZIOHTqizp07KmPffiUmdlFxUUmttsiIcEVEhDf4OftD6zds1JdffqXQ0BAdPpwll8ula6+d6BmnDzw4U48/+oDCw8MkSe8vXCyr1aqBA9JO+zx86Lf3SpJ85COLxSKbzaKU5CS9Pe995ecfU1hYqOfvhYQG68CBQ7p88EANuXygPl21VmvWrldFhUPdErtq2rUTFRQUpLfmzle7dm01csRQlZSc0GMzZ2nK5PEaOmSQCgqP66mnn9GTf35cX6RvbnA9frjtbuj9hZaPT0CcJi2tnz5dtVZpqf20fcdOJSZ2kf8phz3dbrcuuaSnbrrxWlksPpq/4EO98+5C/eKOnyk1pa92795b63Qmt9utl15+Q7GxMXrs0Qfk5+db69zVb775VtdfN0WTJ12l9Rs26a258zXzsQclSVFR7XT//TMUHBSkjZu26pVX5+oPM39ba492Xepbrm3bSE28etxph5x/WHNpaamee/5lXTP1avVNulj79x/UnJff0EMP3lvnHiC0DO3atVVQUKAyMw+qS5dO2rRpq26bflOtANIYezMy9drrb+uWm3/SqL3KS5d9oszMA/r1/TPkb/fXm2+9q48Wr9DkSVdJbrfGjh2prl06q7S0TC/OeU2rV6/TiOFDfuxqogm43W7t+Xafbr5p2mnTfHx8PP9vaPvU0Ou+Z0+G3l+4WL+4/RbFxkZr9Zr1evmVN/WbX9+trOwcrd+wUb998JcKCQ5WQUGhZw9yQ33u3rNXfS7pVWd49fHxkdvt1pyXX9fFvXvqF7ffogMHD+v52a+oXbu26tQxTouXfKyMjEz98p7bFREepkOHs+Tra1Vh4XHNefkN3fLT69Tzom5at36jZs9+VY8/9oBsNpvsdrtu+el1at++nQ4dztJz/35J8Z07qnPnjs306px/LBaLusR30t6M/TVhIyNTCV3jVVRUXLvt5Pamoc/Zuuz5NkP33nOHunTppK937q41TutT3+fhqVwul3Z8tUuSFB3d3tO+e89e3TXjVt10w7Vyu93a+uUOrfrsc911588VERGht+e9p/++NV933PZTJSZ01eYt2zRyxFDtzchUmzaRysjI1NAhg5SxN1Ndu8R7xn9j16Oh99ep71+0XFyEjtP06J6onJw8FRUVK33jFqX94FQEq9Wq/mnJsttt8vPz05jRw5Wxb3+9/WXn5Co7J1dTpoyXv79dVqtVCV3jPdPj4mKUltrv5B6ZVBUUHFd5eYUkKblfH4WGhMhisejS/ikKCLArNzfvjOvwY5f7zpatO9SxQ5yS+/WRxWJR167x6t4tQTt37W50H/CO/mn9tHHTVu0/cEj+/na1bx91Vst/vfMbvf7GPN02/eZGn9Ly+bp0TRg/WqEhIbLZ/DR2zAht/XKHJKl9+yh1S+wqq9Wq0NAQDR1yWYPvF5hRWVmpysrKM+5QaGj71NDr/vm6LzTk8oHq0CFWFotFw4ZepqKiYh07ViCrxaLKyiplHcmRy+VSZGSEZ+90Q32WlZY1WO+RrGwVFh7X2DEjZLVa1bVLZ6WmJGnTpq2SavaUT5w4TpER4fLx8VGnjnGy2+3atv1rJSbE6+LeF8lqterywQPkH2DX7t17JdV8JkRHR3mWSUq6WPsyD5zbC3ABSkjooox9mZKkjH37ldA1Xl27xtdqSzy5zTnbz9n4+E7q0qWTJKnnRd1qjdMfI33jFt13/yP69W8e16uvzdVV40YpKCjQM71jxzjP2Qg+Pj7atPlLDbl8oNq3j5LN5qeJE8fq66+/UXl5hRISu2hf5gFVV1crIyNTw6+4XJn7D8rtdmtvxj4lJn6/nW3sejT0/kLrwBEQnMZisSi5Xx99umqtsrNz1fOibrW+vFdXV2vxko+1fcdOORwO+fj4yOFwqqqqynNe/KmOHy9SWFhovaechIV+/4FqtVpltVrlcDgUEOCvTZu/1Ger16m4uEQWHx8VFZeotLTsjOvwY5f7TmFhoQ4cPKQ/P/k3T5vTWakOHWIb3Qe8o1/fPlq67O9yVbtOC8+NsXr1evXp01udTp4ycSZOZ6VKS0v1+n/fkcVycs+bu2bPodvt1okTpXp/0WLt339QVVUuuapcatOGi9a9zc/PT36+vioqKm5wvvq2T1artcHXvaDwuDL3H1T6xi21+isuLlHXrvGaNHGsPlq8XLl5R9W710WaPOkq2e32BvsMDApssN6iohKFhYbKesp1SZGRETp06IiczkqVlZXXukHD98sVKyKi9piMjIzQ8ZN/68DBw1q8eIXy8o7KLam8vFyDLxvQ4POG0yUmxGvVZ2tVXl6hoqJiRUW1U0REuF57/W2Vl1coKyvHs9PjbD9ngwK/DwcWi6XW5+iP0T8tWTfecI2qq6uVk5On2S++qvDwMCX16S1JCg0NqTV/cVGxIiPCPY9DQ0Lk6+uroqIiRUe3V1BggA4dzlJGxn4NHz5EGzdtVW5unvZmZGrQwP5nvR4Nvb/atm3zo9YZZhFAUKe01H76y//9U0OHDDrt4rKNm77U7t3f6pf33KaQ4GAVFRVr5h/+V+6T0394+DM8PExFRcX1bjjrc+xYod55d6Hu/eUdiouNkST9adb/ye35Sz9uOUsdh2dPrzlc3RITNP3WGxtdL1qGoKBAxXfupI0bt+rq/xl91svffNM0LXj/Iy1b/olGXzn8jPPbbH4KDAzQ9J/fqNjY6NOmL/pgqew2mx568D7ZbH7avGWb5w4z8B4fHx91756grVu3e75Ufcftdp/xNI4zve4R4WFKTelb77US/dOS1T8tWaWlZXpr7gItXfaJrr3m6gb77NE9UavXrNfEq8eeti11u90KCwtRUXGxXC6XJ4QUFBQqLCzUU29+foFCQ2p/eQwLC9Xu3d/WaissOK7wk0dbXnnlTY0dO1KpKX1lsVg0d957DT43qFvnzh3ldFbq83VfKD6+k3x8fGSz2RQZEa7P132h4KAgtWtX8+X5TJ+zZ8Nm81NVlcvzuKysTCEnx0Bdn4enslgsio2NVmJiF+3ctdvzXvFR7eVCw0JVUHjc87ik5ISqqqoUFlZzZC8hsYu2b/9aDqdDkRHhSkzook2bt6m4+ITn+pizcab3F1o+TsFCneLiYnTPXdM1cuTpF+pWVFQoNDREIcHBknTa+fUhIcE6mn/M8zgmur1iottrwXsfyeFwyOVyNeoUlApHhfz8/Dx34Nq5a4+OHj1Wax63pMqqqlr/yivKG1wuOCRIhQXH5XJ9v0H+Yc39+l6ifZn7tXnLNrlcLlVVVSlj3/5aG1i0XFOnjNe9v7xDQUFBZ71scHCQ7rlrujZv3tboe85fNuhSvff+Rzp+vEhSzR7lnbv2SJLKKyrUrl1b2Wx+qqys1Lr1G8+6JjSP8VeN1s5du/X+wsU6evSYnE6nMjMP6vnZrzRq+YZe98sGXapPPl2jgwcPy+12q6LCoS+3fSW3263c3DzPKSn+/nbZ/W2yWi2N6tNms+nFOa/r8OEsORxO5eXla/6CD7Rv337FxkQrPDxcy5Z/KpfLpf37D2rzlm1KSam5pevAAWla9MESFRYel9vt1qFDR+RwONSnT2/tzcjU1zt3q7q6Wms//0Jl5eXq3r3mphsVDodiY6JlsVhUWHhcO3bsbLLX4ELi6+urzp06aNWqz2udhty1a3xN2yl3vzrT5+zZ6BAXq29214yhY8cKPdd0SHV/HkqSW25VV1erqqpKhw9n6dtv9ynmlGtAfiglOUlr1m5QXl6+nM5KLVy0RL179fAcuUhM6Ko1azeo68n17pbYVavXrFfXLp1/1B20Gnp/oXXgCAjqlVjPLe8GXJqi3bu/1dN/fVahISG6+OKLak2/9NIUvfLKW3r4kSfUq2cP/fTmn2j6rTdp/oIP9D9//Ivcbrcu7t2z1ga4LnGxMeqflqyn//qsIiMjFBcb7Tmi8Z3CwuP6zYMza7U9/ugDDS7XvVuC2rSJ1GMzZ8lut+sPM39bZ80z7vy5Fi5confnL5KPj0WdOsbp2muuPotnEN4ScfJOMj9WSEiw7r5rup7914uyWq26YtjgBucfO2aEVny8Sv/45ws6caJUYWGhGjggTb16dte4sSP13zff1fbtX8vf31/dErt4vlDCu2Jjo/Wre+/UR4uX6//+9i9Vu6vVvn1Uo08vauh179EjUZMmjtPct99T/rEC+fvb1a1bVyX16S2Hs1Lz53+go/nH5Ovrq8SEeI2eNPyMfdrtNt33y9v10eIVmv3iqyorK/f8DkiHDnGyWCy67dab9M67C/XoY39WSEiwrp06UZ07dZAkjRs7UouXfKy//2O2yssrFBMdpV/c8TNFRoTr1p/fqEWLluq11+cqun3N3bHs9pq7eU27ZqJee/1thYWFKiQkWD1P3hUOZy8xsYsy9u33fBGXTgaQzz6vdc3ZmT5nz8akSeP05pvz9UX6FoWHhalXrx6eaXV9Hko1dxDcuHGrfHx8FBoaouR+fTTk8oH1/o1+fS9RYWGR/v38y6pwONStW4Kuv37q9+ud0EUOh0OJXWvWsUuXzqqsrPzRtxxu6P2F1sHHTVwEAAAAYAinYAEAAAAwhgACAAAAwBgCCAAAAABjCCAAAAAAjCGAAAAAADCGAAIAAADAGAIIAAAAAGMIIAAAAACMIYAAAAAAMIYAAgAAAMAYAggAAAAAYwggAAAAAIwhgAAAAAAwhgACAAAAwBgCCAAAAABjCCAAAAAAjCGAAAAAADCGAAIAAADAGAIIAAAAAGMIIAAAAACMIYAAAAAAMIYAAgAAAMAYAggAAAAAYwggAAAAAIwhgAAAAAAwhgACAAAAwBgCCAAAAABjCCAAAAAAjCGAAAAAADCGAAIAAADAGAIIAAAAAGMIIAAAAACMIYAAAAAAMIYAAgAAAMAYAggAAAAAYwggAAAAAIwhgAAAAAAwhgACAAAAwBgCCAAAAABjCCAAAAAAjCGAAAAAADDm/wNlz9+NfKMKvwAAAABJRU5ErkJggg==

3.2在线查看Base64图片

Base64/图片转换 - 在线工具 (try8.cn)

 注意添加data:image/png;base64,

 3.3在线修改图表样式

Examples - Apache ECharts

 3.4在线将opt转换为Json

 在线JS对象转JSON工具 - UU在线工具 (uutool.cn)

注意只需要将opt的内容进行转换,不需要带上option=; 

4.结束语

本篇文章到这里就结束了,本文介绍了如何通过Java 第三方库去处理对应的图表数据,以及通过基于PhantomJS的第三方开源项目echartsconvert进行数据转换,获取最后需要的Base64格式的图片数据。有了这个数据可以把它运用到自己需要的地方。比如,写到Word或PDF文档中

将Base64图片通过POI插入Word中 

Echarts图表Java后端生成Base64图片格式,POI写入Base64图片到Word中_青冘的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/853508.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PS的一些智能对象是怎么用的?用于包装设计该怎么使用?

大家都对一些效果图不太理解&#xff0c;我现在就献丑给大家讲一下&#xff0c;教程都是网友盛传的&#xff0c;我自己学习并且有所体会。 一般做的非常好的PS效果图都是外国人自己做的&#xff0c;所以大多数效果图都是英文&#xff0c;细心的网友会发现&#xff0c;中文的是一…

IOS上架流程

IOS上架流程 准备 开发者账号完工的项目 上架步骤 一、创建App ID二、创建证书请求文件 &#xff08;CSR文件&#xff09;三、创建发布证书 &#xff08;CER&#xff09;四、创建Provisioning Profiles配置文件 &#xff08;PP文件&#xff09;五、在App Store创建应用六、…

MPLAB X IDE v6.1.0 使用MCC代码配置器

安装教程应该很好搜到&#xff0c;如果没有 代码配置器&#xff08;MPLAB Code Configurator&#xff0c;MCC&#xff09;说明IDE版本太低了&#xff0c;建议更新下版本&#xff1b; 刚安装完成之后第一次启动会很卡&#xff0c;属于正常现象&#xff1b; 我这好像是离线模式…

博客网站添加复制转载提醒弹窗Html代码

网站如果是完全禁止右键&#xff08;复制、另存为等&#xff09;操作&#xff0c;对用户来说体验感会降低&#xff0c;但是又不希望自己的原创内容直接被copy&#xff0c;今天飞飞和你们分享几行复制转载提醒弹窗Html代码。 效果展示&#xff1a; 复制以下代码&#xff0c;将其…

11_Vue3中的新的组件

1. Fragment 在Vue2中&#xff1a;组件必须要有一个跟标签在Vue3中&#xff1a;组件可以没有根标签&#xff0c;内部会将多个标签包含在一个Fragment虚拟元素中好处&#xff1a;减少标签层级&#xff0c;减少内存占用 2. Teleport 什么是Teleport?——Teleport 是一种能够将…

ubuntu22.04源码编译opencv4.6

第一次编译之后&#xff0c;测试opencv_example不成功&#xff0c;又重新卸载了再装了一次&#xff0c;成功了&#xff0c;简单记录一下。 下载opencv4.6以及opencv_contrib-4.6.0 opencv下载地址 opencv-contrib下载地址解压以后&#xff0c;把contrib放到opencv4.6文件夹下 第…

无涯教程-Perl - getnetent函数

描述 此函数从/etc/networks文件获取下一个条目,返回-($name,$aliases,$addrtype,$net) 如果/etc/networks文件为空,则它将不返回任何内容,并且调用将失败。 语法 以下是此函数的简单语法- getnetent返回值 此函数在错误时返回undef,否则在标量context中返回网络地址,在错…

Qt应用开发(基础篇)——框架类 QFrame

一、前言 QFrame继承于QWidget&#xff0c;被QLCDNumber、QToolBox、QLabel、QListView等部件继承&#xff0c;是一个拥有矩形框架的基类。 QFrame可以直接创建成一个没有内容的的矩形框架&#xff0c;框架的样式由边框厚度(lineWidth)、框架形状(QFrame::Shape)和阴影样式(QFr…

药物临床试验项目风险管理︱科济生物医药临床运营部总监张立峰

科济生物医药&#xff08;上海&#xff09;有限公司临床运营部总监张立峰先生受邀为由PMO评论主办的2023第十二届中国PMO大会演讲嘉宾&#xff0c;演讲议题&#xff1a;药物临床试验项目风险管理---回顾疫情考验下的风险管理应对。大会将于8月12-13日在北京举办&#xff0c;敬请…

小程序外包开发框架

小程序是一种轻量级的移动应用程序&#xff0c;通常用于在微信和其他平台上提供简单的交互和功能。以下是一些常见的小程序开发框架以及它们的特点&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.微…

冯诺依曼体系结构与进程概念理解

目录 1. 先谈硬件 1、初步理解冯诺依曼体系结构 2、理解各个模块之间的协作 2. 再谈软件 1、为什么要有操作系统 2、操作系统如何管理软硬件资源 3. 再谈进程 1、什么是进程 2、如何理解进程 3、Linux 是如何管理进程的 写在最后&#xff1a; 1. 先谈硬件 1、初步理…

亚马逊对AIGC的定义

大家好&#xff0c;这里是Doker,最近AIGC非常火&#xff0c;这里我们聊一下什么是AIGC. 一、 AIGC 介绍与典型行业应用场景 ​AIGC 又称生成式 AI (Generative AI)&#xff0c;是继专业生产内容&#xff08;PGC&#xff0c; Professional-generated Content&#xff09;、用户…

谁知道,多项目管理的进度管理应该如何进行?

大家好&#xff0c;我是老原。多项目管理的进度管理应该如何进行&#xff1f; 我们应该拆分开来看&#xff1a;一手应对多项目管理&#xff0c;一手把握进度管控。 这个和一个人同时需要处理/负责多个项目线程&#xff0c;道理上是通用的。 今天还是会一如即往的在文章里给大…

7.5 批量规范化详解

BatchNorm(归一化/标准化) 归一化/标准化实质是一种线性变换&#xff0c;线性变换有很多良好的性质&#xff0c;这些性质决定了对数据改变后不会造成“失效”&#xff0c;反而能提高数据的表现&#xff0c;这些性质是归一化/标准化的前提。比如有一个很重要的性质&#xff1a;…

webshell免杀项目-webshell(七)

免杀webshell生成工具 项目地址&#xff1a; https://github.com/pureqh/webshell 快速使用 下载该项目后直接用python3运行相关py脚本即可生成免杀代码

如何剪裁操作系统源码——移植FreeRTOS的内存管理模块到ARMV8裸片

如何剪裁操作系统源码 本文面向的需求场景是&#xff0c;为缺乏标准库实现的处理器IP移植内存管理模块&#xff0c;即为裸片部署C标准库中的 malloc() 和 free() 函数。 具体做法是——从操作系统的内存管理组件中剪裁出必要的源码&#xff0c;适配到目标处理器的开发环境&am…

苹果电脑图像元数据编辑器:MetaImage for Mac

MetaImage for Mac是一款功能强大的照片元数据编辑器&#xff0c;它可以帮助用户编辑并管理照片的元数据信息&#xff0c;包括基本信息和扩展信息。用户可以根据需要进行批量处理&#xff0c;方便快捷地管理大量照片。 MetaImage for Mac还提供了多种导入和导出格式&#xff0…

Mysql下载及其安装的详细步骤

1.下载压缩包 官网地址&#xff1a;www.mysql.com 2.环境配置 1.先解压压缩包 2.配置环境变量 添加环境变量&#xff1a;我的电脑--->属性-->高级-->环境变量-->系统变量-->path 3.在mysql安装目录下新建my.ini文件并&#xff0c;编辑my.ini文件 编辑内容如…

最强chatgpt写论文,开源人人可用

大语言模型发展日新月异&#xff0c;有了LLM加持&#xff0c;GPT Researcher已经能帮你写论文了。 GPT Researcher 是一个自主代理程序&#xff0c;旨在进行多种任务的全面在线研究。 该代理能够生成详细、事实性和公正的研究报告&#xff0c;并提供个性化选项&#xff0c;以便…

“黑化”的AI | 盘点十大新型 AI 网络攻击类型

如今&#xff0c;人工智能技术的迅猛发展给各个领域都带来了前所未有的变革和进步。其中当属2023年上半年的“顶流选手”——生成式AI和ChatGPT了。 ChatGPT的火爆出圈&#xff0c;让人们看到了AI惊艳表现的光彩一面&#xff0c;但同时黑暗的一面也正在暗自发力&#xff0c;野…