Skip to content

Monthly statement — generate this PDF with Java (Template)

JQuick-PdfScenario Demos

Template1 页 A4 · 27 KB

Customer statement: auto-paginated detail table, footer totals and page numbers.

Download the PDFView raw template fileBack to all demos

Monthly statement — Template
Rendered output

Output specs

PDF pages
1
Chart type
Template
Template lines
159
Source lines
102

Java source · JQuickBiz72Statement.java

This is the complete test class that produced the PDF above — copy it and run as-is; see the quick-start section for the Maven dependency.

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright (c) [2025-2099] Martin ([email protected])
 */
package com.github.paohaijiao.biz.scenario;

import com.github.paohaijiao.config.JPdfConfig;
import com.github.paohaijiao.executor.JQuickPdfFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

public class JQuickBiz72Statement {

    private static final String PDF_DIR = "D:/test/pdf/";
    private static final String OUT_FILE = PDF_DIR + "72_statement.pdf";
    private static final String TEMPLATE = "report/biz/72_statement.txt";

    public static void main(String[] args) throws IOException {
        new File(PDF_DIR).mkdirs();

        // ---------- 1. 数据(普通 Map,无实体类)----------
        String[][] raw = {
                {"2026-09-01", "8 月平台服务费", "12800.00"},
                {"2026-09-03", "接口调用超量计费(8 月)", "3260.00"},
                {"2026-09-05", "银行转账还款", "-20000.00"},
                {"2026-09-08", "报表模板定制(合同 BJ-2026-0032)", "26000.00"},
                {"2026-09-12", "SVG 图表组件扩容", "4500.00"},
                {"2026-09-15", "技术服务费(9 月上半月)", "6400.00"},
                {"2026-09-18", "票据贴现利息", "-860.00"},
                {"2026-09-20", "银行转账还款", "-30000.00"},
                {"2026-09-22", "数据迁移服务", "32000.00"},
                {"2026-09-25", "账户管理费", "-120.00"},
                {"2026-09-26", "现场部署服务费", "19200.00"},
                {"2026-09-28", "培训服务(4 场)", "19200.00"},
                {"2026-09-30", "结算尾差调整", "-38.50"}
        };
        BigDecimal increase = BigDecimal.ZERO;
        BigDecimal decrease = BigDecimal.ZERO;
        Map<String, Object> data = new LinkedHashMap<String, Object>();
        for (int i = 0; i < raw.length; i++) {
            BigDecimal amount = new BigDecimal(raw[i][2]);
            boolean isIncrease = amount.signum() >= 0;
            if (isIncrease) {
                increase = increase.add(amount);
            } else {
                decrease = decrease.add(amount.abs());
            }
            String suffix = String.valueOf(i + 1);
            data.put("t" + suffix, raw[i][0]);
            data.put("s" + suffix, raw[i][1]);
            data.put("a" + suffix, amount.abs().setScale(2, RoundingMode.HALF_UP).toPlainString());
            data.put("d" + suffix, isIncrease ? "增加" : "减少");
        }
        BigDecimal opening = new BigDecimal("186420.00");
        BigDecimal closing = opening.add(increase).subtract(decrease);

        data.put("customer", "××城市商业银行股份有限公司");
        data.put("month", "2026 年 09 月");
        data.put("monthShort", "09");
        data.put("billNo", "DZ-2026-09-0871");
        data.put("openingBalance", opening.setScale(2, RoundingMode.HALF_UP).toPlainString());
        data.put("totalIncrease", increase.setScale(2, RoundingMode.HALF_UP).toPlainString());
        data.put("totalDecrease", decrease.setScale(2, RoundingMode.HALF_UP).toPlainString());
        data.put("closingBalance", closing.setScale(2, RoundingMode.HALF_UP).toPlainString());

        // ---------- 2. 渲染 ----------
        JPdfConfig config = new JPdfConfig();
        config.getLayoutConfig().setFlexLayout(true);
        config.getDoc().setMargins(Arrays.asList(45, 45, 55, 45));
        config.getFooterConfig().setEnabled(true);
        config.getFooterConfig().setFontSize(8);
        config.getFooterConfig().setPageNumberFormat("第 %d 页");

        JQuickPdfFactory factory = new JQuickPdfFactory(config);
        factory.bindAll(data);
        byte[] pdf = factory.executeResource(TEMPLATE);

        FileOutputStream out = new FileOutputStream(OUT_FILE);
        out.write(pdf);
        out.close();
        System.out.println("已生成:" + OUT_FILE);
    }
}

Minimal invocation (3 lines)

Skip the full test class — the core is these three lines: bind the chart SVG, execute the template, get the PDF bytes. Copy them straight into your project.

// config 可省略;svg 来自 JChartRendererFactory.renderChart(...),见下方完整源码
byte[] pdf = new JQuickPdfFactory(config).executeResource("report/biz/72_statement.txt");

Template file · 72_statement.txt

An HTML-like template with inline CSS that renders to PDF once bound to data. The full template is below — copy it or download the raw file.

<pdf>
<body>
  <h1 style="fontSize:19pt; bold:true; color:#1F4E79; textAlignment:center; characterSpacing:4pt">'客户月度对账单'</h1>
  <p style="fontSize:9pt; color:#5A6B7C; textAlignment:center; marginBottom:8px">'STATEMENT OF ACCOUNT · '${month}</p>

  <table style="width:100%; fontSize:9pt">
    <tr>
      <td style="width:78px; padding:5px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'客户名称'</td>
      <td style="width:180px; padding:5px; border:1px solid #C3CDD8">${customer}</td>
      <td style="width:78px; padding:5px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'对账月份'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${month}</td>
    </tr>
    <tr>
      <td style="padding:5px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'账单编号'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${billNo}</td>
      <td style="padding:5px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'币种'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">'人民币(CNY)'</td>
    </tr>
  </table>

  <table style="width:100%; fontSize:9pt; marginTop:6px">
    <tr>
      <td style="padding:6px; backgroundColor:#F7F9FC; bold:true; color:#1F4E79; border:1px solid #C3CDD8; width:160px">'期初余额('${monthShort}' 月初)'</td>
      <td style="padding:6px; backgroundColor:#F7F9FC; textAlign:right; bold:true; border:1px solid #C3CDD8">'¥'${openingBalance}</td>
    </tr>
  </table>

  <table style="width:100%; fontSize:8.5pt; marginTop:6px">
    <tr>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:34px">'序号'</th>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:110px">'交易时间'</th>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:200px">'项目'</th>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:80px">'金额'</th>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:70px">'方向'</th>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'1'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${t1}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${s1}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a1}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d1}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'2'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${t2}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${s2}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a2}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d2}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'3'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${t3}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${s3}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a3}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d3}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'4'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${t4}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${s4}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a4}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d4}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'5'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${t5}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${s5}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a5}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d5}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'6'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${t6}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${s6}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a6}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d6}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'7'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${t7}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${s7}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a7}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d7}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'8'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${t8}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${s8}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a8}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d8}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'9'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${t9}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${s9}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a9}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d9}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'10'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${t10}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${s10}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a10}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d10}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'11'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${t11}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${s11}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a11}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d11}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'12'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${t12}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${s12}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a12}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d12}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'13'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${t13}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${s13}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${a13}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${d13}</td>
    </tr>
  </table>

  <table style="width:100%; fontSize:9pt; marginTop:4px">
    <tr>
      <td style="padding:4px; border:1px solid #C3CDD8; textAlign:center; backgroundColor:#F7F9FC; color:#5A6B7C; width:25%">'本期增加'</td>
      <td style="padding:4px; border:1px solid #C3CDD8; textAlign:center; backgroundColor:#F7F9FC; color:#5A6B7C; width:25%">'本期减少'</td>
      <td style="padding:4px; border:1px solid #C3CDD8; textAlign:center; backgroundColor:#F7F9FC; color:#5A6B7C; width:25%">'期末余额'</td>
      <td style="padding:4px; border:1px solid #C3CDD8; textAlign:center; backgroundColor:#F7F9FC; color:#5A6B7C; width:25%">'应结金额'</td>
    </tr>
    <tr>
      <td style="padding:5px; border:1px solid #C3CDD8; textAlign:center; bold:true; color:#B71C1C; fontSize:11pt">'¥'${totalIncrease}</td>
      <td style="padding:5px; border:1px solid #C3CDD8; textAlign:center; bold:true; color:#1B5E20; fontSize:11pt">'¥'${totalDecrease}</td>
      <td style="padding:5px; border:1px solid #C3CDD8; textAlign:center; bold:true; color:#1F4E79; fontSize:11pt">'¥'${closingBalance}</td>
      <td style="padding:5px; border:1px solid #C3CDD8; textAlign:center; bold:true; color:#1F4E79; fontSize:11pt">'¥'${closingBalance}</td>
    </tr>
  </table>

  <div style="borderLeft:4px solid #2F5597; background:#F7F9FC; padding:7px 9px; marginTop:8px">
    <p style="fontSize:8.5pt; color:#333333">'说明:本对账单统计 '${month}' 期间全部交易。「本期增加」为借方发生额合计,「本期减少」为贷方发生额合计,期末余额 = 期初余额 + 本期增加 - 本期减少。'</p>
    <p style="fontSize:8.5pt; color:#333333">'请贵司于收到本对账单之日起 7 个工作日内核对并签章回传,逾期未回复视为无异议。'</p>
  </div>

  <div style="display:flex; justifyContent:space-between; marginTop:26px">
    <div style="width:230px; borderTop:1px solid #5A6B7C; paddingTop:4px">
      <p style="fontSize:9pt; color:#5A6B7C; textAlignment:center">'客户方(盖章):      '</p>
    </div>
    <div style="width:230px; borderTop:1px solid #5A6B7C; paddingTop:4px">
      <p style="fontSize:9pt; color:#5A6B7C; textAlignment:center">'财务专用章:        '</p>
    </div>
  </div>
</body>
</pdf>

More demos in this group

The PDF, preview image, template and source code on this page were all produced by a real JQuick-Pdf run and are free to copy into your own project.