Pie Chart of Overdue Loan Share by Bucket

jquick-pdf by example — demo #19 of 184. Every article in this series ships one complete, runnable demo: a template file and a Java class. No snippets, no omissions.

Asset quality review: how the overdue book splits across five classification buckets.

This document belongs to the Business chart series set. Like every demo in the series it has its own colour scheme and layout combination, derived deterministically from the demo number — so the look is stable across runs and two neighbouring documents never look alike.

What you'll build

  • A PDF written to D:/test/biz/19_overdue_loan_pie.pdf
  • A template file (report/biz/19_overdue_loan_pie.txt) with 1 bound placeholder
  • A detail grid of 7 rows × 4 columns, laid out with width:100% so it fills the text area
  • One self-contained Java class (JQuickBiz19OverdueLoanPieTest) that binds the data and renders the file

Bound placeholders

${svg} 

Prerequisites

Java 8+ and one dependency — jquick-pdf is built on Apache PDFBox, not iText:

<dependency> <groupId>io.github.paohaijiao</groupId> <artifactId>jquick-pdfx</artifactId> <version>4.2.0</version> </dependency> 

Coordinates: io.github.paohaijiao:jquick-pdfx:4.2.0.

Step 1 — the template file

JPdfX is jquick-pdf's HTML-like template language. It lives in its own file (src/test/resources/report/biz/19_overdue_loan_pie.txt) instead of a Java string, so designers can edit it without touching code.

<pdf> <body> <div style="background:#FBECEC; border:1px solid #E8C4C4; borderLeft:6px solid #B71C1C; borderRadius:4px; padding:12px; marginBottom:10px; keepTogether:true"> <h1 style="color:#B71C1C; fontSize:17pt; bold:true; marginBottom:4px">'贷款五级分类资产质量报告'</h1> <p style="color:#5A6B7C; fontSize:9pt">'报告编号:CR-2024-Q3-019 | 生成日期:2024年10月8日 | 数据截止:2024年9月30日'</p> </div> <div style="marginBottom:10px"> <div style="borderLeft:4px solid #B71C1C; paddingLeft:8px; marginBottom:6px"> <h2 style="color:#B71C1C; fontSize:13pt; bold:true; marginTop:0px; marginBottom:0px">'一、资产质量概览'</h2> </div> <div style="background:#FBECEC; borderLeft:4px solid #B71C1C; padding:10px; borderRadius:3px; marginBottom:10px"> <p style="fontSize:10pt; color:#333; italic:true">'核心结论:三季度末信贷资产 9,080 笔,正常类占比 75.4%、关注类 13.7%,次级及以下不良资产合计 990 笔、占比 10.9%,整体质量可控,但不良处置压力仍然存在。'</p> </div> <div style="display:flex; justifyContent:center"> <svg style="width:700pt">${svg}</svg> </div> <p style="fontSize:9pt; color:#6c757d; italic:true; marginTop:6px">'图 1 2024年三季度末贷款五级分类构成(数据来源:信贷资产管理系统)'</p> </div> <div style="marginBottom:10px"> <div style="borderLeft:4px solid #B71C1C; paddingLeft:8px; marginBottom:6px"> <h2 style="color:#B71C1C; fontSize:13pt; bold:true; marginTop:0px; marginBottom:0px">'二、五级分类明细'</h2> </div> <table style="width:560px; fontSize:10pt"> <tr> <th style="width:150px; backgroundColor:#B71C1C; color:white; padding:6px; textAlign:center; border:1px solid #E8C4C4">'五级分类'</th> <th style="width:140px; backgroundColor:#B71C1C; color:white; padding:6px; textAlign:center; border:1px solid #E8C4C4">'笔数(笔)'</th> <th style="width:130px; backgroundColor:#B71C1C; color:white; padding:6px; textAlign:center; border:1px solid #E8C4C4">'占比'</th> <th style="width:140px; backgroundColor:#B71C1C; color:white; padding:6px; textAlign:center; border:1px solid #E8C4C4">'风险状态'</th> </tr> <tr> <td style="padding:5px; border:1px solid #E8C4C4">'正常类'</td> <td style="padding:5px; textAlign:right; border:1px solid #E8C4C4">'6,850'</td> <td style="padding:5px; textAlign:right; border:1px solid #E8C4C4">'75.4%'</td> <td style="padding:5px; textAlign:center; color:#1B5E20; bold:true; border:1px solid #E8C4C4">'达标'</td> </tr> <tr> <td style="padding:5px; border:1px solid #E8C4C4">'关注类'</td> <td style="padding:5px; textAlign:right; border:1px solid #E8C4C4">'1,240'</td> <td style="padding:5px; textAlign:right; border:1px solid #E8C4C4">'13.7%'</td> <td style="padding:5px; textAlign:center; color:#BF360C; bold:true; border:1px solid #E8C4C4">'关注'</td> </tr> <tr> <td style="padding:5px; border:1px solid #E8C4C4">'次级类'</td> <td style="padding:5px; textAlign:right; border:1px solid #E8C4C4">'560'</td> <td style="padding:5px; textAlign:right; border:1px solid #E8C4C4">'6.2%'</td> <td style="padding:5px; textAlign:center; color:#BF360C; bold:true; border:1px solid #E8C4C4">'预警'</td> </tr> <tr> <td style="padding:5px; border:1px solid #E8C4C4">'可疑类'</td> <td style="padding:5px; textAlign:right; border:1px solid #E8C4C4">'280'</td> <td style="padding:5px; textAlign:right; border:1px solid #E8C4C4">'3.1%'</td> <td style="padding:5px; textAlign:center; color:#BF360C; bold:true; border:1px solid #E8C4C4">'预警'</td> </tr> <tr> <td style="padding:5px; border:1px solid #E8C4C4">'损失类'</td> <td style="padding:5px; textAlign:right; border:1px solid #E8C4C4">'150'</td> <td style="padding:5px; textAlign:right; border:1px solid #E8C4C4">'1.7%'</td> <td style="padding:5px; textAlign:center; color:#BF360C; bold:true; border:1px solid #E8C4C4">'超标'</td> </tr> <tr> <td style="padding:6px; bold:true; border:1px solid #E8C4C4">'合计'</td> <td style="padding:6px; textAlign:right; bold:true; border:1px solid #E8C4C4">'9,080'</td> <td style="padding:6px; textAlign:right; bold:true; border:1px solid #E8C4C4">'100.0%'</td> <td style="padding:6px; textAlign:center; bold:true; border:1px solid #E8C4C4">'—'</td> </tr> </table> </div> <div style="marginBottom:10px"> <div style="borderLeft:4px solid #B71C1C; paddingLeft:8px; marginBottom:6px"> <h2 style="color:#B71C1C; fontSize:13pt; bold:true; marginTop:0px; marginBottom:0px">'三、风险管控建议'</h2> </div> <list style="paddingLeft:18px; fontSize:10pt" listType="ordered"> <li style="marginBottom:5px">'正常类占据绝对主导:正常类 6,850 笔、占比 75.4%,较关注类高出 61.7 个百分点,基础资产整体稳健。'</li> <li style="marginBottom:5px">'关注类需前置管控:关注类 1,240 笔、占比 13.7%,一旦向下迁徙将直接推高不良,须按月跟踪迁徙率变化。'</li> <li style="marginBottom:5px">'不良处置压力集中:次级 560 笔、可疑 280 笔、损失 150 笔,合计 990 笔、占比 10.9%,建议加快核销与清收进度。'</li> <li style="marginBottom:5px">'总体目标:将关注类占比压降至 12% 以内,次级及以下不良占比控制在 9% 以内。'</li> <li style="marginBottom:5px">'操作建议:对次级及以下 990 笔资产建立“一户一策”清收台账,按季度考核现金回收率。'</li> </list> </div> <p style="textAlignment:center; color:#8a939b; fontSize:8pt; italic:true; marginTop:10px">'本报告仅供信贷资产质量分析使用 | 数据来源:信贷资产管理系统 | 编制部门:风险管理部'</p> </body> </pdf> 

Step 2 — the Java class

jquick-pdfx/src/test/java/com/github/paohaijiao/biz/chart/JQuickBiz19OverdueLoanPieTest.java

/* * 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 (goudingcheng@gmail.com) */ package com.github.paohaijiao.biz.chart; import com.github.paohaijiao.JOption; import com.github.paohaijiao.code.JTrigger; import com.github.paohaijiao.config.JPdfConfig; import com.github.paohaijiao.data.JData; import com.github.paohaijiao.demo.constant.JQuickConstant; import com.github.paohaijiao.enums.JChartType; import com.github.paohaijiao.executor.JQuickPdfFactory; import com.github.paohaijiao.factory.JChartRendererFactory; import com.github.paohaijiao.series.JPie; import org.junit.Test; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; /** * 业务场景 Demo 19:逾期贷款占比(金融 / 信贷平台) * * <p>业务场景:信贷平台按贷款五级分类(正常、关注、次级、可疑、损失)统计资产笔数构成, * 用于资产质量月度例会与风险预警,主色采用风险主题的低饱和深红 #B71C1C。</p> * <p>实际图表类型:{@link JChartType#PIE} 饼图(展示各风险档位的资产占比)。</p> * * <p>模板:{@code report/biz/19_overdue_loan_pie.txt};图表以 SVG 文本形式绑定到模板的 * {@code ${svg}} 占位符,输出到 {@code D:\test\biz\19_overdue_loan_pie.pdf}。</p> * * <p>图表尺寸说明:图表按 700pt × 400pt(宽 × 高)的方形比例设计;模板中 {@code <svg>} * 只声明宽度、不声明高度,因此宽度被页面自动收窄时高度会按原比例同步缩放,不会纵向拉伸变形。</p> * * @author Martin * @version 1.0.0 */ public class JQuickBiz19OverdueLoanPieTest { public static final String path = JQuickConstant.path; @Test public void biz19OverdueLoanPie() throws IOException { JOption option = new JOption(); option.title().text("贷款五级分类构成").subtext("2024年三季度末信贷资产(单位:笔)"); option.tooltip().trigger(JTrigger.item); JPie pie = new JPie("逾期账龄分布"); // 五级分类笔数:正常占绝对多数,次级及以下为关注类风险敞口 pie.data( new JData().name("正常").value(6850), new JData().name("关注").value(1240), new JData().name("次级").value(560), new JData().name("可疑").value(280), new JData().name("损失").value(150) ); option.series(pie); String svg = JChartRendererFactory.renderChart(JChartType.PIE, option); File dir = new File(path + "biz"); if (!dir.exists()) { dir.mkdirs(); } FileOutputStream fileOutputStream = new FileOutputStream(path + "biz/19_overdue_loan_pie.pdf"); JPdfConfig config = new JPdfConfig(); // 开启 flex 行布局,使模板中的 display:flex 生效(图表居中) config.getLayoutConfig().setFlexLayout(true); JQuickPdfFactory factory = new JQuickPdfFactory(config); factory.bind("svg", svg); byte[] bytes = factory.executeResource("report/biz/19_overdue_loan_pie.txt"); fileOutputStream.write(bytes); fileOutputStream.close(); } } 

Step 3 — run it

The class is a JUnit test, so run it with Maven:

mvn -pl jquick-pdfx test -Dtest=JQuickBiz19OverdueLoanPieTest 

…or call the test method from a main wrapper if you want a plain entry point:

public static void main(String[] args) throws Exception { new JQuickBiz19OverdueLoanPieTest().biz19OverdueLoanPie(); } 

Output: D:/test/biz/19_overdue_loan_pie.pdf

How it works

Binding a chart SVG into the template

The chart is not a bitmap — JChartRendererFactory.renderChart(...) returns an SVG string, and the template embeds it with a single <svg> element bound to ${svg}. That means the chart stays vector-sharp at any zoom level, and it participates in normal flow layout instead of being stamped on top of the page.

  • Declare only the width on <svg>; the height is derived from the chart's aspect ratio, so the chart never stretches vertically.
  • Open config.getLayoutConfig().setFlexLayout(true) if the template centers the chart with display:flex.
  • Bind the SVG string with factory.bind("svg", svg) — bind() is pure data injection and never re-parses markup.
String svg = JChartRendererFactory.renderChart(JChartType.LINE, option); JQuickPdfFactory factory = new JQuickPdfFactory(config); factory.bind("svg", svg); byte[] bytes = factory.executeResource(TEMPLATE); 

Making the table fill the text area

Tables default to content width, which leaves a ragged right margin on a formal document. Setting width:100% on the <table> makes it span the full text area; percentage widths on the first row's cells then distribute the remaining space across columns.

  • Set width:100% on <table> and give explicit widths only to the leading columns.
  • Label columns need around 100px so labels like Taxpayer ID do not wrap.
  • Leave the last column without a width so it absorbs rounding remainder.
<table style="width:100%; fontSize:9pt"> <tr> <td style="width:100px; padding:5px">'Taxpayer ID'</td> <td style="padding:5px">${taxNo}</td> </tr> </table> 

Template syntax cheat sheet

Syntax Meaning
'literal text' A single-quoted string is a literal — whatever is inside is printed verbatim.
${var} An expression. It must sit outside the quotes: 'No: '${docNo}.
<table style="width:100%"> Spans the full text area; give explicit widths only to the leading columns.
<areaBreak></areaBreak> Force the next block onto a fresh page (CSS page-break is not supported).
<image src='D:/.../x.png'> Embed an image. src is a raw path — it is not evaluated as an expression.

Gotchas worth remembering

  • Use the full-width ¥, not ¥, or the character may render as ?.
  • Compute money with BigDecimal, round once, and derive 大写 from the rounded value.
  • Flex children whose fixed widths exceed the text area wrap instead of sitting side by side.
  • ${var} must sit outside single quotes — 'No: ${docNo}' prints the placeholder literally.

Output

Wrap up

You now have a template file, a Java class and a PDF — all three reproducible from a clean clone. Swap the placeholder blocks for real data (or for real seal images) and the layout does not move.

Series navigation: ← #18 Geo Chart of Application Volume by Region · #20 Donut Chart of Enterprise Credit Grade Distribution →

Full index: README.md · Source: jquick-pdf

Appendix — files in this demo

Role Path
Java class jquick-pdfx/src/test/java/com/github/paohaijiao/biz/chart/JQuickBiz19OverdueLoanPieTest.java
Template jquick-pdfx/src/test/resources/report/biz/19_overdue_loan_pie.txt
Output PDF D:/test/biz/19_overdue_loan_pie.pdf
Preview image images/19.png