Skip to content

Completion settlement — generate this PDF with Java (Template)

JQuick-PdfScenario Demos

Template2 页 A4 · 71 KB

Multi-page settlement book: perforated seal strip, project name in page headers and paginated detail tables.

Download the PDFView raw template fileBack to all demos

Completion settlement — Template
Rendered output

Output specs

PDF pages
2
Chart type
Template
Template lines
273
Source lines
239

Java source · JQuickBiz81CompletionSettlement.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 javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class JQuickBiz81CompletionSettlement {

    private static final String PDF_DIR = "D:/test/pdf/";
    private static final String IMG_DIR = "D:/test/image/";
    private static final String OUT_FILE = PDF_DIR + "81_completion_settlement.pdf";
    private static final String TEMPLATE = "report/biz/81_completion_settlement.txt";

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

        // ---------- 1. 生成图片(双方公章 + 骑缝章条),模板按字面量路径引用 ----------
        String sealOwner = IMG_DIR + "81_seal_owner.png";
        String sealBuilder = IMG_DIR + "81_seal_builder.png";
        generateSeal(sealOwner, "建设单位", "公章");
        generateSeal(sealBuilder, "施工单位", "公章");
        splitSealForRidge(sealBuilder, 4, IMG_DIR + "81_ridge_");

        // ---------- 2. 组织数据(普通 Map,无实体类)----------
        // 明细行:名称 / 单位 / 工程量 / 综合单价(合价由程序计算)
        String[][] subs = {
                {"土方开挖及回填", "m³", "12860", "38.50"},
                {"C30 钢筋混凝土基础", "m³", "2380", "860.00"},
                {"框架柱、梁混凝土浇筑", "m³", "4120", "920.00"},
                {"钢筋制安(HRB400)", "t", "1860", "5280.00"},
                {"砌体工程(加气混凝土砌块)", "m³", "1560", "420.00"},
                {"屋面防水(SBS 卷材)", "m²", "6800", "96.00"},
                {"外墙保温及真石漆", "m²", "9200", "168.00"},
                {"铝合金门窗安装", "m²", "3400", "520.00"},
                {"给排水管道安装", "m", "5600", "78.00"},
                {"电气桥架及配管", "m", "12800", "46.00"},
                {"消防喷淋系统", "m²", "18600", "62.00"},
                {"通风空调设备安装", "台", "86", "8600.00"},
                {"室内精装修(办公区)", "m²", "7200", "680.00"},
                {"室外道路及绿化", "m²", "5400", "158.00"},
                {"电梯采购与安装", "台", "4", "386000.00"},
                {"弱电智能化系统", "项", "1", "1260000.00"},
                {"临时设施及安全文明施工", "项", "1", "480000.00"}
        };
        String[][] changes = {
                {"QZ-001", "地下室增加抗浮锚杆 126 根", "386000.00"},
                {"QZ-002", "首层大堂石材由国产改为进口", "428000.00"},
                {"QZ-003", "因设计变更空调主机由 3 台增至 4 台", "268000.00"},
                {"QZ-004", "取消原设计中庭采光顶", "-196000.00"}
        };

        Map<String, Object> data = new LinkedHashMap<String, Object>();
        data.put("settlementNo", "JS-2026-0086");
        data.put("projectName", "××市科创中心一期建设工程");
        data.put("ownerUnit", "××市城市建设投资集团有限公司");
        data.put("builderUnit", "××建工集团第三工程有限公司");
        data.put("settlementDate", "2026年10月19日");
        data.put("contractAmount", "¥ 46,800,000.00(肆仟陆佰捌拾万元整)");

        BigDecimal subTotal = BigDecimal.ZERO;
        for (int i = 0; i < subs.length; i++) {
            BigDecimal qty = new BigDecimal(subs[i][2]);
            BigDecimal price = new BigDecimal(subs[i][3]);
            BigDecimal amount = qty.multiply(price).setScale(2, BigDecimal.ROUND_HALF_UP);
            subTotal = subTotal.add(amount);
            data.put("spName" + (i + 1), subs[i][0]);
            data.put("spUnit" + (i + 1), subs[i][1]);
            data.put("spQty" + (i + 1), qty.toPlainString());
            data.put("spPrice" + (i + 1), price.toPlainString());
            data.put("spAmount" + (i + 1), amount.toPlainString());
        }
        BigDecimal changeTotal = BigDecimal.ZERO;
        for (int i = 0; i < changes.length; i++) {
            BigDecimal amount = new BigDecimal(changes[i][2]);
            changeTotal = changeTotal.add(amount);
            data.put("chNo" + (i + 1), changes[i][0]);
            data.put("chContent" + (i + 1), changes[i][1]);
            data.put("chAmount" + (i + 1), amount.toPlainString());
        }
        data.put("chTotal", changeTotal.toPlainString());
        BigDecimal total = subTotal.add(changeTotal).setScale(2, BigDecimal.ROUND_HALF_UP);
        data.put("totalWords", amountInWords(total));
        data.put("totalPlain", total.toPlainString());
        data.put("compiler", "张国强");
        data.put("reviewer", "李慧");
        data.put("approver", "王建平");

        // ---------- 3. 渲染 ----------
        JPdfConfig config = new JPdfConfig();
        config.getLayoutConfig().setFlexLayout(true);
        // 右侧留 26pt 供骑缝章细条使用
        config.getDoc().setMargins(Arrays.asList(58, 76, 55, 50));
        config.getHeaderConfig().setEnabled(true);
        config.getHeaderConfig().setText(data.get("projectName") + " 竣工结算书");
        config.getHeaderConfig().setFontSize(8);
        config.getHeaderConfig().setHeight(34);
        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);
        java.nio.file.Files.write(new File(OUT_FILE).toPath(), pdf);
        System.out.println("已生成:" + OUT_FILE);
    }

    /**
     * 人民币大写(到分),与发票场景保持同一套换算规则。
     */
    public static String amountInWords(BigDecimal amount) {
        String[] digits = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
        String[] units = {"", "拾", "佰", "仟"};
        String[] groups = {"", "万", "亿"};
        BigDecimal scaled = amount.setScale(2, BigDecimal.ROUND_HALF_UP);
        long integer = scaled.longValue();
        int fraction = scaled.subtract(new BigDecimal(integer)).movePointRight(2).intValue();
        String number = String.valueOf(integer);
        StringBuilder result = new StringBuilder();
        int groupIndex = 0;
        boolean pendingZero = false;
        while (number.length() > 0) {
            int end = Math.max(0, number.length() - 4);
            String group = number.substring(end);
            number = number.substring(0, end);
            if (Integer.parseInt(group) == 0) {
                pendingZero = result.length() > 0;
                groupIndex++;
                continue;
            }
            StringBuilder part = new StringBuilder();
            for (int i = 0; i < group.length(); i++) {
                int d = group.charAt(i) - '0';
                int pos = group.length() - 1 - i;
                if (d == 0) {
                    if (part.length() > 0 && !part.toString().endsWith("零")) {
                        part.append("零");
                    }
                } else {
                    part.append(digits[d]).append(units[pos]);
                }
            }
            String text = part.toString();
            if (text.endsWith("零")) {
                text = text.substring(0, text.length() - 1);
            }
            if (pendingZero) {
                result.insert(0, "零");
                pendingZero = false;
            }
            result.insert(0, text + groups[groupIndex]);
            groupIndex++;
        }
        if (result.length() == 0) {
            result.append("零");
        }
        StringBuilder words = new StringBuilder(result).append("元");
        if (fraction == 0) {
            words.append("整");
        } else {
            int jiao = fraction / 10;
            int fen = fraction % 10;
            if (jiao > 0) {
                words.append(digits[jiao]).append("角");
            } else if (fen > 0) {
                words.append("零");
            }
            if (fen > 0) {
                words.append(digits[fen]).append("分");
            }
        }
        return words.toString();
    }

    /** 生成简化公章(圆环 + 上方角色名 + 下方「公章」字样)。 */
    private static void generateSeal(String path, String top, String bottom) throws IOException {
        int size = 200;
        BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        Color red = new Color(0xC6, 0x28, 0x28);
        g.setColor(red);
        g.setStroke(new BasicStroke(6f));
        g.drawOval(8, 8, size - 16, size - 16);
        g.setFont(new Font("Microsoft YaHei", Font.BOLD, 17));
        g.drawString(top, size / 2 - 34, 66);
        g.setFont(new Font("Microsoft YaHei", Font.BOLD, 16));
        g.drawString(bottom, size / 2 - 16, 132);
        g.dispose();
        ImageIO.write(image, "png", new File(path));
    }

    /**
     * 骑缝章:把公章图按纵向切成 strips 条,输出到固定前缀路径。
     * 打印后沿装订线裁切,各页边缘的细条拼起来就是一枚完整的章。
     */
    public static List<String> splitSealForRidge(String sealPath, int strips, String prefix) throws IOException {
        BufferedImage source = ImageIO.read(new File(sealPath));
        int stripWidth = Math.max(1, source.getWidth() / strips);
        List<String> paths = new ArrayList<String>();
        for (int i = 0; i < strips; i++) {
            int x = i * stripWidth;
            int w = Math.min(stripWidth, source.getWidth() - x);
            BufferedImage part = source.getSubimage(x, 0, w, source.getHeight());
            String path = prefix + i + ".png";
            ImageIO.write(part, "png", new File(path));
            paths.add(path);
        }
        return paths;
    }
}

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/81_completion_settlement.txt");

Template file · 81_completion_settlement.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:20pt; bold:true; color:#1A2733; textAlignment:center; characterSpacing:6pt">'工程竣工结算书'</h1>
  <p style="fontSize:9pt; color:#5A6B7C; textAlignment:center; marginBottom:10px">'结算书编号:'${settlementNo}</p>

  <!-- 封面信息 -->
  <table style="width:100%; fontSize:9.5pt">
    <tr>
      <td style="width:110px; padding:6px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'工程名称'</td>
      <td style="padding:6px; border:1px solid #C3CDD8">${projectName}</td>
    </tr>
    <tr>
      <td style="padding:6px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'建设单位'</td>
      <td style="padding:6px; border:1px solid #C3CDD8">${ownerUnit}</td>
    </tr>
    <tr>
      <td style="padding:6px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'施工单位'</td>
      <td style="padding:6px; border:1px solid #C3CDD8">${builderUnit}</td>
    </tr>
    <tr>
      <td style="padding:6px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'结算日期'</td>
      <td style="padding:6px; border:1px solid #C3CDD8">${settlementDate}</td>
    </tr>
    <tr>
      <td style="padding:6px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'合同金额'</td>
      <td style="padding:6px; border:1px solid #C3CDD8">${contractAmount}</td>
    </tr>
  </table>

  <!-- 一、分项工程结算明细 -->
  <h2 style="fontSize:11pt; bold:true; color:#1F4E79; borderLeft:3px solid #1F4E79; paddingLeft:6px; marginTop:10px; marginBottom:5px">'一、分项工程结算明细'</h2>
  <table style="width:100%; fontSize:8.5pt">
    <tr>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:36px">'序号'</th>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79">'分项工程名称'</th>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:40px">'单位'</th>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:64px">'工程量'</th>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:76px">'综合单价'</th>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:88px">'合价(元)'</th>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'1'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName1}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit1}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty1}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice1}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount1}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'2'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName2}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit2}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty2}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice2}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount2}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'3'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName3}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit3}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty3}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice3}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount3}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'4'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName4}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit4}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty4}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice4}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount4}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'5'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName5}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit5}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty5}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice5}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount5}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'6'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName6}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit6}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty6}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice6}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount6}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'7'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName7}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit7}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty7}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice7}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount7}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'8'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName8}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit8}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty8}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice8}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount8}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'9'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName9}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit9}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty9}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice9}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount9}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'10'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName10}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit10}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty10}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice10}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount10}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'11'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName11}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit11}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty11}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice11}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount11}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'12'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName12}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit12}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty12}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice12}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount12}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'13'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName13}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit13}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty13}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice13}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount13}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'14'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName14}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit14}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty14}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice14}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount14}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'15'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName15}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit15}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty15}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice15}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount15}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'16'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName16}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit16}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty16}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice16}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount16}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">'17'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${spName17}</td>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${spUnit17}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spQty17}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spPrice17}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${spAmount17}</td>
    </tr>
  </table>

  <!-- 二、签证变更清单 -->
  <h2 style="fontSize:11pt; bold:true; color:#1F4E79; borderLeft:3px solid #1F4E79; paddingLeft:6px; marginTop:10px; marginBottom:5px">'二、签证变更清单'</h2>
  <table style="width:100%; fontSize:8.5pt">
    <tr>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:70px">'编号'</th>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79">'变更内容'</th>
      <th style="padding:5px; backgroundColor:#1F4E79; color:white; textAlign:center; border:1px solid #1F4E79; width:110px">'金额(元)'</th>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${chNo1}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${chContent1}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${chAmount1}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${chNo2}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${chContent2}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${chAmount2}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${chNo3}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${chContent3}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${chAmount3}</td>
    </tr>
    <tr>
      <td style="padding:5px; textAlign:center; border:1px solid #C3CDD8">${chNo4}</td>
      <td style="padding:5px; border:1px solid #C3CDD8">${chContent4}</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8">${chAmount4}</td>
    </tr>
    <tr>
      <td style="padding:5px; border:1px solid #C3CDD8; bold:true">'签证变更合计'</td>
      <td style="padding:5px; border:1px solid #C3CDD8">'—'</td>
      <td style="padding:5px; textAlign:right; border:1px solid #C3CDD8; bold:true">${chTotal}</td>
    </tr>
  </table>

  <!-- 三、结算总金额 -->
  <h2 style="fontSize:11pt; bold:true; color:#1F4E79; borderLeft:3px solid #1F4E79; paddingLeft:6px; marginTop:10px; marginBottom:5px">'三、结算总金额'</h2>
  <table style="width:100%; fontSize:9.5pt">
    <tr>
      <td style="width:100px; padding:6px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'大写金额'</td>
      <td style="padding:6px; border:1px solid #C3CDD8; bold:true; color:#B71C1C">${totalWords}</td>
    </tr>
    <tr>
      <td style="padding:6px; backgroundColor:#E8EDF2; bold:true; border:1px solid #C3CDD8">'小写金额'</td>
      <td style="padding:6px; border:1px solid #C3CDD8; bold:true; color:#B71C1C">'¥'${totalPlain}</td>
    </tr>
  </table>
  <p style="fontSize:8.5pt; color:#5A6B7C; marginTop:6px">'说明:本结算书依据施工合同、竣工图纸、现场签证及变更指令编制,经建设、施工双方核对确认后作为竣工结算与工程款支付的依据。'</p>

  <!-- 四、编制与审核 -->
  <h2 style="fontSize:11pt; bold:true; color:#1F4E79; borderLeft:3px solid #1F4E79; paddingLeft:6px; marginTop:10px; marginBottom:5px">'四、编制与审核'</h2>
  <div style="display:flex; justifyContent:space-between; marginTop:4px">
    <div style="width:32%; borderTop:1px solid #5A6B7C; paddingTop:4px">
      <p style="fontSize:9pt; color:#5A6B7C; textAlignment:center">'编制人:'${compiler}</p>
      <p style="fontSize:8pt; color:#8a939b; textAlignment:center; marginTop:16px">'(签字)'</p>
    </div>
    <div style="width:32%; borderTop:1px solid #5A6B7C; paddingTop:4px">
      <p style="fontSize:9pt; color:#5A6B7C; textAlignment:center">'审核人:'${reviewer}</p>
      <p style="fontSize:8pt; color:#8a939b; textAlignment:center; marginTop:16px">'(签字)'</p>
    </div>
    <div style="width:32%; borderTop:1px solid #5A6B7C; paddingTop:4px">
      <p style="fontSize:9pt; color:#5A6B7C; textAlignment:center">'审批人:'${approver}</p>
      <p style="fontSize:8pt; color:#8a939b; textAlignment:center; marginTop:16px">'(签字)'</p>
    </div>
  </div>

  <!-- 双方公章 -->
  <div style="display:flex; justifyContent:space-between; marginTop:18px">
    <div style="width:49%">
      <p style="fontSize:9pt; color:#5A6B7C">'建设单位(盖章)'</p>
      <p style="fontSize:9pt; bold:true; color:#1A2733">${ownerUnit}</p>
      <image src='D:/test/image/81_seal_owner.png' style="width:92px; height:92px"></image>
    </div>
    <div style="width:49%">
      <p style="fontSize:9pt; color:#5A6B7C">'施工单位(盖章)'</p>
      <p style="fontSize:9pt; bold:true; color:#1A2733">${builderUnit}</p>
      <image src='D:/test/image/81_seal_builder.png' style="width:92px; height:92px"></image>
    </div>
  </div>

  <!-- 骑缝章细条 -->
  <div style="marginTop:10px">
    <p style="fontSize:7.5pt; color:#8a939b">'骑缝章条(打印后沿右侧装订线裁切,与各页边缘切口拼合形成骑缝章):'</p>
    <div style="display:flex; justifyContent:space-between; marginTop:3px">
      <image src='D:/test/image/81_ridge_0.png' style="width:14px; height:92px"></image>
      <image src='D:/test/image/81_ridge_1.png' style="width:14px; height:92px"></image>
      <image src='D:/test/image/81_ridge_2.png' style="width:14px; height:92px"></image>
      <image src='D:/test/image/81_ridge_3.png' style="width:14px; height:92px"></image>
    </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.