spring-boot使用itext与jfree导出pdf报表

时间:2022-10-26 22:18:26

          报表在项目开发中经常使用,itext是生成报表的java组件,对比于之前使用的poi,itext侧重于pdf格式报表,并结合jfreechar制作图表,能够为用户多样化显示数据。本文结合项目应用案例使用进行描述。


        首先用一张图来显示生成的报表样式:

spring-boot使用itext与jfree导出pdf报表

       1. pom文件导入itext引用

  <!-- https://mvnrepository.com/artifact/org.jfree/jfreechart -->
        <dependency>
            <groupId>org.jfree</groupId>
            <artifactId>jfreechart</artifactId>
            <version>1.0.19</version>
        </dependency>

        <!--<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        -->
        <!-- itext方式导出pdf -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.4</version>

            <!-- <version>5.5.6</version>-->


            <!-- After release 2.1.7, iText moved from the MPLicense to the AGPLicense.
                The groupId changed from com.lowagie to com.itextpdf and the artifactId from itext to itextpdf.
                See http://itextpdf.com/functionalitycomparison for more information
               <groupId>com.lowagie</groupId>
              <artifactId>itext</artifactId>
              <version>4.2.2</version>
             <version>4.2.2</version>-->

        </dependency>
        <dependency>
            <groupId>com.lowagie</groupId>
            <artifactId>itext</artifactId>
            <version>2.1.7</version>
            <!-- <version>4.2.2</version>-->

        </dependency>
        <!-- https://mvnrepository.com/artifact/com.lowagie/itext-rtf -->
        <dependency>
            <groupId>com.lowagie</groupId>
            <artifactId>itext-rtf</artifactId>
            <version>2.1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
           <!-- <version>3.16</version>-->
            <version>3.7</version>


        </dependency>
        <!-- https://mvnrepository.com/artifact/com.itextpdf/itext-asian -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>

    2.  文本样式导入方法

BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);//设置中文字体
Font font = new Font(bfChinese, 10);
Font headFont = new Font(bfChinese, 14, Font.BOLD);//设置字体
Font littleHeadFont = new Font(bfChinese, 12, Font.BOLD);
Document document = new com.itextpdf.text.Document(PageSize.A4,
        MARGIN_OF_ONE_CM, MARGIN_OF_ONE_CM, MARGIN_OF_ONE_CM, MARGIN_OF_ONE_CM);  //设置A4纸张样式,每页填充满后自动换行
PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());
//设置文本属性
document.addAuthor("gskj");
document.addSubject("Subject");
document.addLanguage("chinese");
document.addCreationDate();
document.addCreator("whp");
document.addTitle("title");
writer.setTagged();
writer.createXmpMetadata();
document.open();
//定义段落
Paragraph paragraph = new Paragraph();
//设置段落前后间距
paragraph.setSpacingAfter(10);
paragraph.setSpacingBefore(10);
paragraph.setFont(font);
ReportVo overView = exportVo.getOverView();
Paragraph bigTitle = new Paragraph("一、概述", headFont);
StringBuffer overViewBuffer = new StringBuffer("在2018年1月22号对大数据系统的审计总体状况如下:共有");
overViewBuffer.append(overView.getUserNumber());
overViewBuffer.append("个用户使用,进行了");
overViewBuffer.append(overView.getVisitsTotal());
overViewBuffer.append("次访问,出现高风险事件");
overViewBuffer.append(overView.getHighEventNumber());
overViewBuffer.append("次;该大数据系统的基本状况如下:");
paragraph.add(overViewBuffer.toString());
document.add(bigTitle);
document.add(paragraph); //document文件流中添加数据

3. 使用jFreeChart导出图表,前期经过一番实验,利用itext接口直接导入jfreechart图表只提供了绝对定位的接口(也许是没有找到相对定位),当表格数据或者图表数据无法确定大小的情况下,样式不好控制,因此采用一种巧妙的方式,将jfreeChart转化为图片,并将图片作为document元素导入,样式更加美观可控。

/**
 * 柱状图
 *
 * @param dataset         数据集
 * @param xName           x轴的说明(如种类,时间等)
 * @param yName           y轴的说明(如速度,时间等)
 * @param chartTitle      图标题
 * @param charName        生成图片的名字
 * @param plotOrientation 方向
 * @return
 */
public JFreeChart createBarChart(CategoryDataset dataset, String xName,
                                 String yName, String chartTitle, String charName, PlotOrientation plotOrientation) {
    JFreeChart chart = ChartFactory.createBarChart(chartTitle, // 图表标题
            xName, // 目录轴的显示标签
            yName, // 数值轴的显示标签
            dataset, // 数据集
            plotOrientation, // 图表方向:水平、垂直
            true, // 是否显示图例(对于简单的柱状图必须是false)
            false, // 是否生成工具
            false // 是否生成URL链接
    );
    java.awt.Font labelFont = new java.awt.Font("STSong-Light", java.awt.Font.PLAIN, 16);
    /*
     * VALUE_TEXT_ANTIALIAS_OFF表示将文字的抗锯齿关闭,
     * 使用的关闭抗锯齿后,字体尽量选择12到14号的宋体字,这样文字最清晰好看
     */
    // chart.getRenderingHints().put(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
    chart.setTextAntiAlias(false);
    chart.setBackgroundPaint(Color.white);
    // create plot
    CategoryPlot plot = chart.getCategoryPlot();
    // 设置横虚线可见
    plot.setRangeGridlinesVisible(true);
    // 虚线色彩
    plot.setRangeGridlinePaint(Color.gray);
    // 数据轴精度
    NumberAxis vn = (NumberAxis) plot.getRangeAxis();
    // vn.setAutoRangeIncludesZero(true);
    DecimalFormat df = new DecimalFormat("#0.00");
    vn.setNumberFormatOverride(df); // 数据轴数据标签的显示格式
    // x轴设置
    CategoryAxis domainAxis = plot.getDomainAxis();
    domainAxis.setLabelFont(labelFont);// 轴标题
    domainAxis.setTickLabelFont(labelFont);// 轴数值
    domainAxis.setMaximumCategoryLabelWidthRatio(0.6f);// 横轴上的 Lable 是否完整显示
    // 设置距离图片左端距离
    domainAxis.setLowerMargin(0.1);
    // 设置距离图片右端距离
    domainAxis.setUpperMargin(0.1);
    // 设置 columnKey 是否间隔显示
    // domainAxis.setSkipCategoryLabelsToFit(true);
    plot.setDomainAxis(domainAxis);
    // 设置柱图背景色(注意,系统取色的时候要使用16位的模式来查看颜色编码,这样比较准确)
    plot.setBackgroundPaint(new Color(255, 255, 204));
    // y轴设置
    ValueAxis rangeAxis = plot.getRangeAxis();
    rangeAxis.setLabelFont(labelFont);
    rangeAxis.setTickLabelFont(labelFont);
    // 设置最高的一个 Item 与图片顶端的距离
    rangeAxis.setUpperMargin(0.15);
    // 设置最低的一个 Item 与图片底端的距离
    rangeAxis.setLowerMargin(0.15);
    plot.setRangeAxis(rangeAxis);
    BarRenderer renderer = new BarRenderer();
    // 设置柱子宽度
    renderer.setMaximumBarWidth(0.05);
    // 设置柱子高度
    renderer.setMinimumBarLength(0.2);
    // 设置柱子边框颜色
    renderer.setBaseOutlinePaint(Color.BLACK);
    // 设置柱子边框可见
    renderer.setDrawBarOutline(true);
    // // 设置柱的颜色
    renderer.setSeriesPaint(0, new Color(204, 255, 255));
    renderer.setSeriesPaint(1, new Color(153, 204, 255));
    renderer.setSeriesPaint(2, new Color(51, 204, 204));
    // 设置每个地区所包含的平行柱的之间距离
    renderer.setItemMargin(0.0);
    // 显示每个柱的数值,并修改该数值的字体属性
    renderer.setIncludeBaseInRange(true);
    renderer.setBaseItemLabelGenerator(new StandardCategoryItemLabelGenerator());
    renderer.setBaseItemLabelsVisible(true);
    renderer.setBaseItemLabelFont(labelFont);
    if(plotOrientation.equals(PlotOrientation.HORIZONTAL)){ //控制结果数据在柱状图的位置
        renderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE3, TextAnchor.BASELINE_LEFT));
    }
    plot.setRenderer(renderer);
    // 设置柱的透明度
    plot.setForegroundAlpha(1.0f);
    FileOutputStream fos_jpg = null;
    try {
        isChartPathExist(CHART_PATH);
        String chartName = CHART_PATH + charName;
        fos_jpg = new FileOutputStream(chartName);
        ChartUtilities.writeChartAsPNG(fos_jpg, chart, 500, 500, true, 10);
        return chart;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    } finally {
        try {
            fos_jpg.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/**
 * 判断文件夹是否存在,如果不存在则新建
 *
 * @param chartPath
 */
private void isChartPathExist(String chartPath) {
    File file = new File(chartPath);
    if (!file.exists()) {
        file.mkdirs();
        // log.info("CHART_PATH="+CHART_PATH+"create.");
    }
}

示例代码:

double[][] data = new double[][]{
     {672, 766, 223, 540, 126}
};
String[] rowKeys = {"苹果"};
String[] columnKeys = {"北京", "上海", "广州", "成都", "深圳"};
CategoryDataset datasetBar = pm.getBarData(data, rowKeys, columnKeys);
JFreeChart chart =pm.createBarChart(datasetBar, "x坐标", "y坐标", "柱状图", "bar.png");
File fileName = new File("/root/jfreechart2.pdf");
JFreeChart chart = createBarChart(datasetBar, "", "", "", ".png", PlotOrientation.HORIZONTAL);
Image logoImage = Image.getInstance(ChartUtilities.encodeAsPNG(chart.createBufferedImage(1080, 920))); //转化为图片
logoImage.scaleAbsolute(500, 400); //设置图片大小
logoImage.setAccessibleAttribute(PdfName.ALT, new PdfString("Logo"));
document.add(logoImage);

4. 制做表格

private PdfPTable createTable(int numCloumns, int numRows, String[][] data, Font font) {
        PdfPTable table = new PdfPTable(numCloumns);
        table.setWidthPercentage(100); // 宽度100%填充
        table.setSpacingBefore(10f); // 前间距
        table.setSpacingAfter(10f); // 后间距
        ArrayList<PdfPRow> listRow = table.getRows();

        for (int i = 0; i < numRows; i++) {
            //行1
            PdfPCell cells[] = new PdfPCell[numCloumns];
            PdfPRow row = new PdfPRow(cells);
            for (int j = 0; j < numCloumns; j++) {
                cells[j] = new PdfPCell(new Paragraph(data[i][j], font));
            }
            listRow.add(row);
        }

        return table;
    }

举例:

array5[0][0] = "语句类型";
array5[0][1] = "语句数";
for (int i = 1; i < statementActionList.size() + 1; i++) {
     array5[i][0] = statementActionList.get(i - 1).getStatementType();  //第一列数据
     array5[i][1] = statementActionList.get(i - 1).getStatementNumber(); //第二列数据
}
PdfPTable table5 = createTable(2, statementActionList.size() + 1, array5, font);
document.add(table5);