java freemark生成doc报表(包含表格、图片)

1、技术方案

  • freemark生成doc
  • word文本中的图片通过调用phantomjs执行echarts,返回图片的base64编码。

2、具体步骤

2.1创建word模板

创建一个word文档,设计好报表内容及格式,使用占位符替换掉内容。

2.2word另存为xml

  • 这里选择word2013,没有选择word2003,因为2003图片一致无法渲染成功。

  • 使用nodpad++格式化打开xml,手动调整被分割开的xml文档。

  • 把xml模板中图片base64编码替换为占位符,其中图片的编号对象在模板的图片的顺序,例如image10.png,代表模板中第10张图片。

  • 在表格循环的地方使用<#list datalist as item><#/list>

调整好xml内容后,修改后缀改为.ftl。

2.3phantomjs执行echarts生成图片

phantomjs安装教程网上还多,这里不再说明,注意执行js时不支持es6语法。编写生成图片的echarts.html,返回生成的图片base64字符串。

etharts.html内容为:

<html>
    <meta charset="UTF-8">
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
<head>
<script src="./jquery-3.4.1.min.js"></script>
<script src="./echarts.min.js"></script>
</head>
<body>
    <div id="charsDom" style="height:350px;width:700px"></div>
</body>
<script>
    var charsDom = null ; 

    function getPie(param){
        var title = param.title , data = param.data;
        var option = {
            title : {
                text: title,
                x:'center'
            },
            animation:false,
            tooltip : {
                trigger: 'item',
                formatter: "{a} <br/>{b} : {c} ({d}%)"
            },
            legend: {
                orient: 'vertical',
                left: 'left',
            },
            series : [
                {
                    name: title,
                    type: 'pie',
                    radius : '55%',
                    center: ['50%', '60%'],
                    data:data,
                    label:{
                        formatter:"{b}:({d}%)"
                    },
                    itemStyle: {
                        emphasis: {
                            shadowBlur: 10,
                            shadowOffsetX: 0,
                            shadowColor: 'rgba(0, 0, 0, 0.5)'
                        }
                    }
                }
            ]
        }
        return option;
    }

    function getBar(param){
        var title = param.title , data = param.data;
        var option = {
            title:{
                text:title,
                 x:'center'
            },
            animation:false,
            color: ['#3398DB'],
            tooltip : {
                trigger: 'axis',
                axisPointer : {            // 坐标轴指示器,坐标轴触发有效
                    type : 'shadow'        // 默认为直线,可选为:'line' | 'shadow'
                }
            },
            grid: {
               left: '10px',
                right: '50px',
                bottom: '10px',
                top: '10px',
                containLabel: true
            },
            yAxis : [{
               type: 'category',
                minInterval: 1,
                min:0,
                data: data.yData,
                axisTick:{
                    interval:0
                },
                axisLabel: {
                    interval: 0,
                },
                max:function (value) {
                    if (value.max < 10 ){
                        value.max = 10;
                    } else {
                        value.max = value.max;
                    }
                    return value.max;
                },
                scale:true
            }],
            xAxis : [{
                    name:"数量",
                    type: 'value',
                    boundaryGap: true,
                    min:0,
                    minInterval: 1,
                    axisTick:{
                        interval:0
                    },
                    axisLabel: {
                        interval: 0,
                        rotate: 0,
                    },
            }],
            series : [
                {
                    name:title,
                    type:'bar',
                    barWidth: '60%',
                    data:data.xData,
                    label: {
                        normal: {
                            position: 'right',
                            show: true
                        }
                    },
                }
            ]
        };
        return option;
    }

    function noData(){
        var option = {
            graphic:[
                {
                    type: 'rect', left: 'center', top: 'center', z: 100,
                    shape: {
                        width: 700,
                        height: 350
                    },
                    style: {
                        fill:'#f5f5f9'
                    }
                },
                {
                    type: 'text', left: 'center', top: 'center', z: 100,
                    style: {
                        fill: '#999',
                        text: '暂 无 数 据 ',
                        font: '18px Microsoft YaHei'
                    }
                }
            ],
            series:[]
        };
        var el = document.getElementById('charsDom');
        if (charsDom !=null) {
             echarts.dispose(document.getElementById('charsDom'));
        }
        charsDom = echarts.init(document.getElementById('charsDom'));
        charsDom.setOption(option);

        // 将第一个画布作为基准。
        var baseCanvas = $(el).find("canvas").first()[0];
        if (!baseCanvas) {
            return false;
        };
        var width = el.width;
        var height = el.height;
        var ctx = baseCanvas.getContext("2d");
        //遍历,将后续的画布添加到在第一个上
        $(el).find("canvas").each(function(i, canvasObj) {
            if (i > 0) {
                var canvasTmp = $(canvasObj)[0];
                ctx.drawImage(canvasTmp, 0, 0, width, height);
            }
        });
        console.log(baseCanvas.toDataURL('image/png',0.5).replace("data:image/png;base64,",""));
        //获取base64位的url
        return baseCanvas.toDataURL('image/png',0.5).replace("data:image/png;base64,","");
    }



    function returnEchartImg(echartObj){
        return charsDom.getDataURL();
    }
   
    function getImage(param , fun){
        var el = document.getElementById('charsDom');
        if (charsDom !=null) {
             echarts.dispose(document.getElementById('charsDom'));
        }
        charsDom = echarts.init(document.getElementById('charsDom'));
        charsDom.setOption(fun(param));

        // 将第一个画布作为基准。
        var baseCanvas = $(el).find("canvas").first()[0];
        if (!baseCanvas) {
            return false;
        };
        var width = el.width;
        var height = el.height;
        var ctx = baseCanvas.getContext("2d");
        //遍历,将后续的画布添加到在第一个上
        $(el).find("canvas").each(function(i, canvasObj) {
            if (i > 0) {
                var canvasTmp = $(canvasObj)[0];
                ctx.drawImage(canvasTmp, 0, 0, width, height);
            }
        });
        console.log(baseCanvas.toDataURL('image/png',0.5).replace("data:image/png;base64,",""));
        //获取base64位的url
        return baseCanvas.toDataURL('image/png',0.5).replace("data:image/png;base64,","");
    }
</script>
</html>

2.4生成模板

通过java shell调用phantomjs执行etharts.html

//创建无界面的浏览器对象
    private static PhantomJSDriver getPhantomJSDriver() {
        //设置必要参数
        DesiredCapabilities dcaps = new DesiredCapabilities();
        dcaps.setCapability("acceptSslCerts", true);
        dcaps.setCapability("takesScreenshot", true);
        dcaps.setCapability("cssSelectorsEnabled", true);
        dcaps.setJavascriptEnabled(true);
        dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY, "/usr/bin/phantomjs");
        //创建无界面浏览器对象
        return new PhantomJSDriver(dcaps);
    }

    //获取报表图的base64编码
    public static String getImage(String data, String function) {
        WebDriver driver = null;
        try {
            driver = getPhantomJSDriver();
            driver.get("file:///root/echarts/echarts.html");
            driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
            JavascriptExecutor js = (JavascriptExecutor) driver;
            if (data.length() == 0) {
                return (String) js.executeScript("return noData()");
            } else {
                return (String) js.executeScript("return getImage(" + data + "," + function + ")");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (driver != null) {
                driver.close();
                driver.quit();
            }
        }
        return null;
    }

定义模板数据类,并生成模板,数据类的变量名与我们模板中占位变量名相同。

public class ReportModel{
    private List<xx> xxxList;
    private String date;
}
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_21);
        configuration.setClassForTemplateLoading(this.getClass(), "/template/");
        configuration.setClassicCompatible(true);
        configuration.setDefaultEncoding("UTF-8");
        Template template = configuration.getTemplate("报表模板.ftl");
        String wordName = projectId + "_" + taskId + ".doc";
        File outFile = new File(taskFolderPath + "/" + wordName);
        Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "utf-8"));
        template.process(reportModel, writer);
        writer.close();

生成word如下:

但是生成的doc文档,底层模式的还是xml,转化为pdf时,结果pdf的内容全部为xml,暂时没有解决这个问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Freemarker 本身并不支持生成图片,它是一个模板引擎,用于生成文本文件。但是,你可以使用 Java 的图形库(如 AWT 或 JavaFX)来生成图片,然后将图片插入到模板中。 以下是一个简单的示例,使用 AWT 绘制一张图片并将其插入到 Freemarker 模板中: Java 代码: ```java import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; public class ImageGenerator { public static void generateImage(String text, String outputFileName) throws IOException { // 创建 BufferedImage 对象 BufferedImage image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_RGB); // 获取 Graphics2D 对象 Graphics2D g2d = image.createGraphics(); // 设置画布背景色为白色 g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, 200, 100); // 设置字体和颜色 g2d.setColor(Color.BLACK); g2d.drawString(text, 50, 50); // 保存图片 ImageIO.write(image, "png", new File(outputFileName)); } } ``` Freemarker 模板: ```html <html> <body> <h1>欢迎使用 Freemarker</h1> <img src="${imagePath}" /> </body> </html> ``` Java 代码: ```java import freemarker.template.Configuration; import freemarker.template.Template; import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) throws IOException { // 生成图片 ImageGenerator.generateImage("Hello, Freemarker!", "image.png"); // 创建 Freemarker 配置对象 Configuration cfg = new Configuration(Configuration.VERSION_2_3_31); cfg.setDirectoryForTemplateLoading(new File("templates")); // 加载模板文件 Template template = cfg.getTemplate("index.ftl"); // 设置模板变量 Map<String, Object> data = new HashMap<>(); data.put("imagePath", "image.png"); // 渲染模板 StringWriter out = new StringWriter(); template.process(data, out); // 输出渲染结果 System.out.println(out.toString()); } } ``` 在这个示例中,我们创建了一个名为 `ImageGenerator` 的类,其中包含一个名为 `generateImage` 的静态方法,该方法接受要绘制的文本和输出文件名作为参数。该方法使用 AWT 创建一个 `BufferedImage` 对象,并在图像上绘制文本。最后,它将图像保存为 PNG 文件。 在 `Main` 类中,我们使用 `ImageGenerator.generateImage` 方法生成一张图片,并将图片路径传递给 Freemarker 模板中的变量 `imagePath`。然后,我们加载模板文件并使用 `Template.process` 方法渲染模板,将变量 `imagePath` 替换为实际的图片路径。最后,我们将渲染结果输出到控制台。 请注意,这只是一个简单的示例,实际情况可能更为复杂。你需要根据自己的需求进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值