SpringBoot自定义HTML生成PDF,支持水印文本及图片

SpringBoot自定义HTML生成PDF,支持水印文本及图片

根据HTML实现数据渲染填充PDF动态列表,表格,图片

步骤1、Pom.xml 引入

   <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-pdf-itext5</artifactId>
            <version>9.1.22</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.13</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>

        <!-- freemarker -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>

步骤2、编写工具类

@Component
@Slf4j
public class FreeMarkUtils {
    private FreeMarkUtils() {
    }

    private volatile static Configuration configuration;

    static {
        if (configuration == null) {
            synchronized (FreeMarkUtils.class) {
                if (configuration == null) {
                    configuration = new Configuration(Configuration.VERSION_2_3_28);
                }
            }
        }
    }

    /**
     * 使用 Freemarker 引擎渲染 HTML
     *
     * @param dataMap  传入 HTML 模板的 Map 数据
     * @param fileName 模板文件名称 (例如 "templates/template.ftl")
     * @return 渲染后的 HTML 字符串
     */
    public static String freemarkerRender(Map<String, Object> dataMap, String fileName) {
        // 创建 FreeMarker 配置实例
        Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        // 设置默认字符集和处理异常方法
        configuration.setDefaultEncoding("UTF-8");
        configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        try {
            // 使用 SpringTemplateLoader 读取资源文件目录
            final SpringTemplateLoader templateLoader = new SpringTemplateLoader(new DefaultResourceLoader(), "classpath:templates");
            configuration.setTemplateLoader(templateLoader);
            // 配置 FreeMarker 日志和错误处理
            configuration.setLogTemplateExceptions(false);
            configuration.setWrapUncheckedExceptions(true);
            // 获取模板
            Template template = configuration.getTemplate(fileName);
            // 填充数据并返回结果字符串
            return FreeMarkerTemplateUtils.processTemplateIntoString(template, dataMap);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 如果出现异常或者其他错误,返回 null
        return null;
    }


    /**
     * 将HTML转换为带水印的PDF文档
     *
     * @param htmlTmpStr HTML字符串
     * @param fontFile   字体文件路径
     * @return 带水印的PDF文档字节数组
     */
    public static byte[] createHtml2Pdf(String htmlTmpStr, String fontFile) {
        ByteArrayOutputStream outputStream = null; // 创建一个字节数组输出流
        byte[] result = null; // 初始化返回结果为null
        try {
            outputStream = new ByteArrayOutputStream(); // 实例化字节数组输出流
            ITextRenderer renderer = new ITextRenderer(); // 创建ITextRenderer对象
            renderer.setDocumentFromString(htmlTmpStr); // 将HTML字符串解析为文档对象
            ITextFontResolver fontResolver = renderer.getFontResolver(); // 获取字体解析器
            final File tempFile = getTempFile(fontFile); // 获取临时字体文件
            fontResolver.addFont(tempFile.getAbsolutePath(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); // 将字体文件添加到字体解析器中
            renderer.layout(); // 计算文档的布局
            renderer.createPDF(outputStream); // 将文档转换成PDF并写入字节数组输出流中
            result = outputStream.toByteArray(); // 将生成的PDF字节数组赋值给返回结果
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.flush(); // 刷新字节数组输出流
                    outputStream.close(); // 关闭字节数组输出流
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result; // 返回生成的PDF字节数组,如果出现异常则返回null
    }

    /**
     * @Author Fuzp
     * @Date 11:11 2023/10/10
     * @Content 添加文字水印
     **/
    public static byte[] PDFAddWatermark(byte[] inputFile, String waterMarkName) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        try {
            // 水印的高和宽(参数可调节)
            int textH = 75; // 水印的高度
            int textW = 170; // 水印的宽度
            // 间隔距离(参数可调节)
            int interval = 60; // 水印之间的间隔距离

            PdfReader reader = new PdfReader(inputFile); // 创建PdfReader对象,读取输入文件
            PdfStamper stamper = new PdfStamper(reader, outputStream); // 创建PdfStamper对象,用于修改PDF
            BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED); // 创建字体对象,指定字体样式和编码方式
            PdfGState gs = new PdfGState(); // 创建PdfGState对象,用于设置图形状态
            gs.setFillOpacity(0.2f); // 设置透明度
            gs.setStrokeOpacity(0.4f); // 设置边框透明度
            int total = reader.getNumberOfPages() + 1; // 获取PDF总页数
            JLabel label = new JLabel(); // 创建JLabel对象,用于获取字体的相关信息
            label.setText(waterMarkName); // 设置水印文字
            PdfContentByte under;

            // 可添加多个水印
            Rectangle pageRect = null; // 创建Rectangle对象,存储页面的尺寸信息
            FontMetrics metrics; // 创建FontMetrics对象,用于获取字体的度量信息
            label.setText(waterMarkName); // 设置水印文字
            metrics = label.getFontMetrics(label.getFont()); // 获取字体的度量信息
            for (int i = 1; i < total; i++) {
                pageRect = reader.getPageSizeWithRotation(i); // 获取指定页面的尺寸信息(包括旋转角度)
                // 在内容上方加水印
                under = stamper.getOverContent(i); // 获取内容上方的PdfContentByte对象,用于绘制水印
                // 在内容下方加水印
                // under = stamper.getUnderContent(i);
                under.saveState(); // 保存当前图形状态
                under.setGState(gs); // 设置图形状态
                under.beginText(); // 开始绘制文本内容
                //设置字体颜色为蓝色
                under.setColorFill(BaseColor.BLUE);
                under.setFontAndSize(base, 20); // 设置字体和字号
                // 水印文字成30度角倾斜
                for (int height = interval + textH; height < pageRect.getHeight();
                     height = height + textH * 3) { // 按行循环绘制水印
                    for (int width = interval + textW; width < pageRect.getWidth() + textW;
                         width = width + textW * 2) { // 按列循环绘制水印
                        under.showTextAligned(
                                Element.ALIGN_LEFT, waterMarkName, width - textW, height - textH, 30); // 绘制水印文字
                    }
                }
                under.endText(); // 结束绘制文本内容
            }
            stamper.close(); // 关闭PdfStamper对象
            reader.close(); // 关闭PdfReader对象

        } catch (Exception e) {
            e.printStackTrace();
        }

        return outputStream.toByteArray(); // 返回输出流的字节数组
    }

    /**
     * @Author Fuzp
     * @Date 11:30 2023/10/10
     * @Content 添加水印图片
     **/
    public static byte[] PFAddWatermarkImage(byte[] inputFile, String watermarkImagePath) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        try {
            // 水印的高和宽(参数可调节)
            int imgH = 170; // 水印图片的高度
            int imgW = 170; // 水印图片的宽度
            // 间隔距离(参数可调节)
            int interval = 60; // 水印之间的间隔距离

            PdfReader reader = new PdfReader(inputFile); // 创建PdfReader对象,读取输入文件
            PdfStamper stamper = new PdfStamper(reader, outputStream); // 创建PdfStamper对象,用于修改PDF
            int total = reader.getNumberOfPages() + 1; // 获取PDF总页数
            Image image = Image.getInstance(watermarkImagePath); // 创建Image对象,读取水印图片
            image.scaleAbsolute(imgW, imgH); // 设置水印图片大小
            image.setAbsolutePosition(0, 0); // 设置水印图片位置

            PdfContentByte under;

            Rectangle pageRect = null; // 创建Rectangle对象,存储页面的尺寸信息
            for (int i = 1; i < total; i++) {
                pageRect = reader.getPageSizeWithRotation(i); // 获取指定页面的尺寸信息(包括旋转角度)
                // 在内容上方加水印
                under = stamper.getOverContent(i); // 获取内容上方的PdfContentByte对象,用于绘制水印
                // 在内容下方加水印
                // under = stamper.getUnderContent(i);

                float x = pageRect.getRight() - imgW - interval; // 计算水印图片的横坐标位置
                float y = pageRect.getTop() - imgH - interval; // 计算水印图片的纵坐标位置

                PdfGState gState = new PdfGState();
                gState.setFillOpacity(0.5f); // 设置透明度为0.5
                under.setGState(gState); // 应用透明度

                under.addImage(image, imgW, 0, 0, imgH, x, y); // 绘制水印图片并设置透明度
            }
            stamper.close(); // 关闭PdfStamper对象
            reader.close(); // 关闭PdfReader对象

        } catch (Exception e) {
            e.printStackTrace();
        }

        return outputStream.toByteArray(); // 返回输出流的字节数组
    }


    /**
     * 获取资源文件的临时文件
     * 资源文件打jar包后,不能直接获取,需要通过流获取生成临时文件
     *
     * @param fileName 文件路径 temp/temp.ftl
     * @return
     */
    public static File getTempFile(String fileName) {
        final File tempFile = new File(fileName);
        InputStream fontTempStream = null;
        try {
            Resource resource = new ClassPathResource(fileName);
            fontTempStream = resource.getInputStream();
            System.out.println(fontTempStream);
            FileUtils.copyInputStreamToFile(fontTempStream, tempFile);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fontTempStream != null) {
                    fontTempStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return tempFile;
    }


    /**
     * 导出 PDF 文件
     *
     * @param response HTTP 响应
     * @param fileName 导出的 PDF 文件名
     * @param pdfBytes PDF 字节数组
     */
    public static void exportPDF(HttpServletResponse response, String fileName, byte[] pdfBytes) {
        response.setContentType("application/pdf");
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
        try {
            response.getOutputStream().write(pdfBytes);
            response.getOutputStream().flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 下载文件
     *
     * @param response 相应
     * @param data     数据
     * @param fileName 文件名
     */
    public static void generateFile(HttpServletResponse response, String fileName, byte[] data) {
        response.setHeader("content-Type", "application/octet-stream");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName);
        try {
            response.getOutputStream().write(data);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                response.getOutputStream().close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

步骤3、自定义HTML模版

注:需要前端配合自定义模版计算高度 否则会导致边框 分页失效

<!DOCTYPE html>
<html>
<head>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"></link>
    <style type="text/css">
        body {
            font-family: SimSun;
            background: #ffffff;
            margin: 0px;
            padding: 0px;
        }

        html, body, div, p {
            font-size: 14px;
            margin: 0px;
            padding: 0px;
        }

        @page {
            size: A4 portrait; /*设置纸张大小:A4(210mm 297mm)、A3(297mm 420mm) 横向则反过来*/
            padding: 0;
            margin: 0 0 10mm 0;
            @bottom-center {
                /*content: "第" counter(page) "页  共 " counter(pages) "页";*/
                content: "第" counter(page) "页";
                font-family: SimSun;
                font-size: 4.236mm;
                color: #000;
            };
        }

        @media print {
            html, body {
                width: 210mm;
                height: 287mm;
            }
        }

        /***************************************************************************************/

        .root {
            background: #ffffff;
            width: 196mm;
            height: 273mm;
            margin: 0mm;
            padding: 7mm;
            box-sizing: content-box;
            overflow: scroll;
            font-family: SimSun;
        }

        .clearfix {
            /* 兼容ie */
            *zoom: 1
        }

        /* 当前元素的前后,添加一个空白,且将当前元素转换成table类型 */
        .clearfix:before, .clearfix:after {
            content: " ";
            display: table
        }

        .clearfix:after {
            clear: both
        }

        .font-size-10 {
            font-size: 3.53mm;
        }

        .font-size-12 {
            font-size: 4.236mm;
        }

        .font-size-14 {
            font-size: 4.924mm;
        }

        .font-size-16 {
            font-size: 5.65mm;
        }

        .mar-top-10 {
            margin-top: 3.53mm;
        }

        .mar-top-15 {
            margin-top: 5.295mm;
        }

        .mar-top-25 {
            margin-top: 8.825mm;
        }

        .mar-left-24 {
            margin-left: 8.47mm;
        }

        .title {
            font-weight: bold;
            color: #2C7EFB;
            text-align: center;
        }

        .sub {
            text-align: center;
        }

        .sub .label {
            /*font-size: 10px;*/
            font-size: 3.53mm;
            font-weight: 500;
            color: #686868;
        }

        .sub .value {
            /*font-size: 10px;*/
            font-weight: 500;
            color: #000000;
        }

        .page-break-before {
            page-break-before: always;
        }

        .block-wrap {
            page-break-inside: avoid;
            page-break-after: auto;
        }

        .label-wrap {
            font-weight: bold;
            color: #333333;
        }

        .label-wrap .tag {
            display: block;
            float: left;
            width: 1.059mm;
            height: 4.236mm;
            background: #025DF4;
            margin-right: 1mm;
            margin-top: 1.5mm;
        }

        /*******表格********/

        table, table tr th, table tr td {
            border-collapse: collapse;
            border: 1px solid #000000;
            vertical-align: middle !important;
            white-space: normal;
            word-break: break-all;
            word-wrap: break-word
        }


        table tr th {
            font-weight: 500 !important;
            color: #333333;
        }

        table tr td {
            color: #111111;
        }

        .table-wrap {
            width: 100%;
            text-align: center;
            display: table;
            table-layout: fixed;
        }

        .table-wrap .tr {
            page-break-inside: avoid;
            page-break-after: auto;
            font-size: 3.53mm;
        }

        .table-wrap .tr th {
            background: #F9F9FB;
            padding: 2mm;
            white-space: normal;
            word-break: break-all;
            word-wrap: break-word;
        }

        .table-wrap .tr td {
            padding: 2mm;
            white-space: normal;
            word-break: break-all;
            word-wrap: break-word;
        }

        .table-wrap .tr2 {
            font-size: 3.53mm;
        }

        .table-wrap .tr2 th {
            background: #F9F9FB;
            width: 27.416mm;
            padding-right: 3mm;
            text-align: right;
        }

        .table-wrap .tr2 td {
            padding: 2.5mm;
            text-align: left;
        }

        .ckb-wrap .item {
            display: inline;
            margin-right: 6mm;
        }

        .ckb-wrap .item img {
            width: 4.236mm;
            height: 4.236mm;
            margin-top: 2mm;
            margin-right: 0.5mm;
        }

        .img-list-gz {
            padding-top: 4mm;
            padding-left: 4mm;
        }

        .img-list-gz img {
            width: 40mm;
            height: 40mm;
            margin-right: 4mm;
            margin-bottom: 4mm;
        }

        .img-wrap {
            border: 1px solid #000000;
            margin-top: 2mm;
            padding-top: 4mm;
            padding-left: 4mm;
        }

        .img-wrap img {
            width: 42mm;
            height: 48mm;
            margin-right: 4mm;
            margin-bottom: 4mm;
        }

        .img-list-zy {
            padding-left: 4mm;
            padding-top: 4mm;
        }

        .img-list-zy .item {
            width: 58mm;
            margin-right: 4mm;
            margin-bottom: 4mm;
            float: left;
        }

        .img-list-zy .item img {
            width: 100%;
            height: 58mm;
        }

        .img-list-zy .item .name {
            width: 100%;
            margin: 1mm 2mm;
            font-size: 3.53mm;
            text-align: center;
        }

    </style>
</head>
<body>

<div class="root">
    <div class="title font-size-16">报告</div>
    <div class="label-wrap font-size-14 mar-top-15">
        <span class="tag"></span><span>基本信息</span>
    </div>
    <table class="table-wrap mar-top-10">
        <tr class="tr2">
            <th class="label">自定义</th>
            <td> <#if workorderCode??>
                    ${workorderCode?html}
                </#if>
            </td>
            <th class="label">自定义</th>
            <td>
                <#if launchPersonName??>
                    ${launchPersonName?html} <#if launchPersonMobile??>/${launchPersonMobile?html}</#if>
                </#if>
            </td>
        </tr>
        <tr class="tr2">
            <th class="label">自定义</th>
            <td>
                <#if customerName??>
                    ${customerName?html}
                </#if>
            </td>
            <th class="label">自定义</th>
            <td>
                <#if stationName??>
                    ${stationName?html}
                </#if>
            </td>
        </tr>
        <tr class="tr2">
            <th class="label">自定义</th>
            <td>
                <#if policyNo??>
                    ${policyNo?html}
                </#if>
            </td>
            <th class="label">自定义</th>
            <td>
                <#if insurerName??>
                    ${insurerName?html}
                </#if>
            </td>
        </tr>
        <tr class="tr2">
            <th class="label">自定义</th>
            <td>
                <#if contactPerson??>
                    ${contactPerson?html}/${contactPhone?html}
                </#if>
            </td>
            <th class="label">自定义</th>
            <td>
                <#if appointmentTime??>
                    ${appointmentTime?html}
                </#if>
            </td>
        </tr>
        <tr class="tr2">
            <th class="label">自定义</th>
            <td>
                <#if workorderLevelName??>
                    ${workorderLevelName?html}
                </#if>
            </td>
            <th class="label">自定义</th>
            <td>
                <#if launchTime??>
                    ${launchTime?html}
                </#if>
            </td>
        </tr>
        <tr class="tr2">
            <th class="label">自定义</th>
            <td>
                <#if registerTime??>
                    ${registerTime?html}
                </#if>
            </td>
            <th class="label">自定义</th>
            <td>
                <#if assignTime??>
                    ${assignTime?html}
                </#if>
            </td>
        </tr>
        <tr class="tr2">
            <th class="label">自定义</th>
            <td>
                <#if supplierName??>
                    ${supplierName?html}
                </#if>
            </td>
            <th class="label">自定义</th>
            <td>
                <span>
                    <#if servicePersonName??>
                        ${servicePersonName?html}
                    </#if>
                </span>
            </td>
        </tr>
        <tr class="tr2">
            <th class="label">自定义</th>
            <td>
                <#if startTime??>
                    ${startTime?html}
                </#if>
            </td>
            <th class="label">自定义</th>
            <td>
                <#if finishTime??>
                    ${finishTime?html}
                </#if>
            </td>
        </tr>
        <tr class="tr2">
            <th class="label">自定义</th>
            <td colspan="3">
                <#if reportDescription??>
                    ${reportDescription?html}
                </#if>
            </td>
        </tr>
        <tr class="tr2">
            <th class="label">自定义</th>
            <td colspan="3">
                <#if stationAddress??>
                    ${stationAddress?html}
                </#if>
            </td>
        </tr>
    </table>

    <#if (attachmentList?size > 0)>
        <div class="block-wrap">
            <div class="label-wrap font-size-14 mar-top-15">
                <span class="tag"></span><span>自定义</span>
            </div>
            <div class="img-wrap">
                <#list attachmentList as img>
                    <img src="${img.url}"/>
                </#list>
            </div>
        </div>
    </#if>
    <div class="block-wrap">
        <div class="label-wrap font-size-14 mar-top-15">
            <span class="tag"></span><span>自定义</span>
        </div>
        <table class="table-wrap mar-top-10">
            <tr class="tr">
                <td colspan="2">自定义</td>
                <td colspan="6" style="text-align: left;">
                    <div class="ckb-wrap">
                        <div class="item">
                            <#if shootingResult??>
                                <#if shootingResult == '1' >
                                    <img src="https://dneeedgeimgpubtest.oss-cn-shenzhen.aliyuncs.com/img/icon_report_ckb_sel.png"/>
                                <#else>
                                    <img src="https://dneeedgeimgpubtest.oss-cn-shenzhen.aliyuncs.com/img/icon_report_ckb_nal.png"/>
                                </#if>
                            </#if>
                            <span>正常</span>
                        </div>
                        <div class="item">
                            <#if shootingResult??>
                                <#if shootingResult == '2' >
                                    <img src="https://dneeedgeimgpubtest.oss-cn-shenzhen.aliyuncs.com/img/icon_report_ckb_sel.png"/>
                                <#else>
                                    <img src="https://dneeedgeimgpubtest.oss-cn-shenzhen.aliyuncs.com/img/icon_report_ckb_nal.png"/>
                                </#if>
                            </#if>
                            <span>异常</span>
                        </div>
                    </div>
                </td>
            </tr>
            <tr class="tr">
                <td colspan="2">自定义</td>
                <td colspan="6" style="text-align: left;">
                    <#if verifyDescription??>
                        ${verifyDescription?html}
                    </#if>
                </td>
            </tr>
            <tr class="tr">
                <td colspan="2">自定义</td>
                <td colspan="6" style="text-align: left;">
                    <#if shootingDescription??>
                        ${shootingDescription?html}
                    </#if>
                </td>
            </tr>
            <tr class="tr">
                <td colspan="2">自定义</td>
                <td colspan="6" style="text-align: left;">
                    <div class="img-list-gz">
                        <#if finishAttachmentList??>
                            <#list finishAttachmentList as finishImg>
                                <img src="${finishImg.realUrl}"/>
                            </#list>
                        </#if>
                    </div>
                </td>
            </tr>
        </table>
    </div>

    <#if (userList?size > 0)>
        <div class="block-wrap">
            <div class="label-wrap font-size-14 mar-top-15">
                <span class="tag"></span><span>自定义</span>
            </div>
            <table class="table-wrap mar-top-10">
                <tr class="tr">
                    <th>名称</th>
                    <th>地址</th>
                    <th>手机号</th>
                </tr>
                <#list userList as user>
                    <tr class="tr">
                        <td>
                            <#if user.name??>
                                ${user.name?html}
                            </#if>
                        </td>
                        <td>
                            <#if user.address??>
                                ${user.address?html}
                            </#if>
                        </td>
                        <td>
                            <#if user.phone??>
                                ${user.phone?html}
                            <#else>——
                            </#if>
                        </td>
                    </tr>
                </#list>
            </table>
        </div>
    </#if>

</div>

</body>
</html>

步骤4、Controller实现

@RestController
@RequestMapping("/test")
public class TestController {
    
    @GetMapping("/basic")
    public String test() throws IOException, DocumentException {
        Map<String, Object> dataMap = new HashMap<>(16);
        dataMap.put("workorderCode","23423423");
        dataMap.put("launchPersonName","23423432423");
        dataMap.put("customerName","234324324");
        dataMap.put("stationName","234324324");
        dataMap.put("policyNo","23423");
        dataMap.put("insurerName","234324");
        dataMap.put("contactPerson","111111111111111111111111111111111111111");
        dataMap.put("contactPhone","12324343443");
        dataMap.put("appointmentTime","32432432");
        dataMap.put("workorderLevelName","234234");
        dataMap.put("launchTime","23432432");
        dataMap.put("registerTime","32423423");
        dataMap.put("assignTime","234234234");
        dataMap.put("supplierName","23432423");
        dataMap.put("servicePersonName","23423423");
        dataMap.put("startTime","23423423");
        dataMap.put("finishTime","23423432");
        dataMap.put("reportDescription","23423423");
        dataMap.put("stationAddress","23423423");

        JSONArray attachmentArray = new JSONArray();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("url","https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");
        attachmentArray.add(jsonObject);
        dataMap.put("attachmentList",attachmentArray);
        
        dataMap.put("shootingResult","1");
        
        dataMap.put("verifyDescription","verifyDescription");
        dataMap.put("shootingDescription","shootingDescription");

        JSONArray finishAttachmentArray = new JSONArray();
        JSONObject object = new JSONObject();
        object.put("realUrl","https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");
        finishAttachmentArray.add(object);
        dataMap.put("finishAttachmentList",finishAttachmentArray);

        List<User> userList = new ArrayList<>();
        userList.add(new User(1L,"Tuzq","抚州","16607091111"));
        userList.add(new User(2L,"Fuzp","进贤","16607091111"));
        
        dataMap.put("userList",userList);
        

        String htmlStr = FreeMarkUtils.freemarkerRender(dataMap, "01-basic.ftl");
        byte[] pdfBytes = FreeMarkUtils.createHtml2Pdf(htmlStr, "simsun.ttc");
        byte[] bytes = FreeMarkUtils.PDFAddWatermark(pdfBytes, "哈啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊");
        PdfUtils.savePdfToFile(bytes,"D:\\FreeMarkUtils-水印文件.pdf");

        byte[] bytes2 = FreeMarkUtils.PFAddWatermarkImage(pdfBytes, "src/main/resources/icon.png");
        PdfUtils.savePdfToFile(bytes2,"D:\\FreeMarkUtils-水印图片.pdf");
        return "";
    }

    @GetMapping("/exportPDF")
    public String exportPDF(HttpServletResponse response) throws IOException, DocumentException {
        Map<String, Object> dataMap = new HashMap<>(16);
        dataMap.put("workorderCode","23423423");
        dataMap.put("launchPersonName","23423432423");
        dataMap.put("customerName","234324324");
        dataMap.put("stationName","234324324");
        dataMap.put("policyNo","23423");
        dataMap.put("insurerName","234324");
        dataMap.put("contactPerson","111111111111111111111111111111111111111");
        dataMap.put("contactPhone","12324343443");
        dataMap.put("appointmentTime","32432432");
        dataMap.put("workorderLevelName","234234");
        dataMap.put("launchTime","23432432");
        dataMap.put("registerTime","32423423");
        dataMap.put("assignTime","234234234");
        dataMap.put("supplierName","23432423");
        dataMap.put("servicePersonName","23423423");
        dataMap.put("startTime","23423423");
        dataMap.put("finishTime","23423432");
        dataMap.put("reportDescription","23423423");
        dataMap.put("stationAddress","23423423");

        JSONArray attachmentArray = new JSONArray();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("url","https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");
        attachmentArray.add(jsonObject);
        dataMap.put("attachmentList",attachmentArray);

        dataMap.put("shootingResult","1");

        dataMap.put("verifyDescription","verifyDescription");
        dataMap.put("shootingDescription","shootingDescription");

        JSONArray finishAttachmentArray = new JSONArray();
        JSONObject object = new JSONObject();
        object.put("realUrl","https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");
        finishAttachmentArray.add(object);
        dataMap.put("finishAttachmentList",finishAttachmentArray);

        List<User> userList = new ArrayList<>();
        userList.add(new User(1L,"Tuzq","抚州","16607091111"));
        userList.add(new User(2L,"Fuzp","进贤","16607091111"));

        dataMap.put("userList",userList);


        String htmlStr = FreeMarkUtils.freemarkerRender(dataMap, "01-basic.ftl");
        byte[] pdfBytes = FreeMarkUtils.createHtml2Pdf(htmlStr, "simsun.ttc");
        FreeMarkUtils.exportPDF(response,"FreeMarkUtils.pdf",pdfBytes);
        return "";
    }


    @GetMapping("/generateFile")
    public String generateFile(HttpServletResponse response) throws IOException {
        Map<String, Object> dataMap = new HashMap<>(16);
        dataMap.put("workorderCode","23423423");
        dataMap.put("launchPersonName","23423432423");
        dataMap.put("customerName","234324324");
        dataMap.put("stationName","234324324");
        dataMap.put("policyNo","23423");
        dataMap.put("insurerName","234324");
        dataMap.put("contactPerson","111111111111111111111111111111111111111");
        dataMap.put("contactPhone","12324343443");
        dataMap.put("appointmentTime","32432432");
        dataMap.put("workorderLevelName","234234");
        dataMap.put("launchTime","23432432");
        dataMap.put("registerTime","32423423");
        dataMap.put("assignTime","234234234");
        dataMap.put("supplierName","23432423");
        dataMap.put("servicePersonName","23423423");
        dataMap.put("startTime","23423423");
        dataMap.put("finishTime","23423432");
        dataMap.put("reportDescription","23423423");
        dataMap.put("stationAddress","23423423");

        JSONArray attachmentArray = new JSONArray();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("url","https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");
        attachmentArray.add(jsonObject);
        dataMap.put("attachmentList",attachmentArray);

        dataMap.put("shootingResult","1");

        dataMap.put("verifyDescription","verifyDescription");
        dataMap.put("shootingDescription","shootingDescription");

        JSONArray finishAttachmentArray = new JSONArray();
        JSONObject object = new JSONObject();
        object.put("realUrl","https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png");
        finishAttachmentArray.add(object);
        dataMap.put("finishAttachmentList",finishAttachmentArray);

        List<User> userList = new ArrayList<>();
        userList.add(new User(1L,"Tuzq","抚州","16607091111"));
        userList.add(new User(2L,"Fuzp","进贤","16607091111"));

        dataMap.put("userList",userList);


        String htmlStr = FreeMarkUtils.freemarkerRender(dataMap, "01-basic.ftl");
        byte[] pdfBytes = FreeMarkUtils.createHtml2Pdf(htmlStr, "simsun.ttc");
        FreeMarkUtils.generateFile(response,"FreeMarkUtils.pdf",pdfBytes);
        return "";
    }
    

}

步骤5 Yml设置


  freemarker:
    cache: false  #关闭模板缓存,方便测试
    settings:
      template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
    suffix: .ftl               #指定Freemarker模板文件的后缀名

文章最顶上有代码文件

最终实现效果

水印文字PDF
水印图片

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Spring Boot中给MultipartFile类型的图片添加水印可以通过以下步骤实现: 1. 首先,确保已经引入了相关的依赖,例如spring-boot-starter-web和spring-boot-starter-validation。 2. 创建一个自定义水印图片,该图片可以是任何带有透明背景的图片,可以使用图片处理工具如Adobe Photoshop或在线图片编辑器来创建。 3. 在Spring Boot应用的Controller中创建一个POST请求的处理方法,接收MultipartFile类型的图片参数和其他需要的参数。 4. 使用Java的ImageIO类将上传的MultipartFile类型的图片换成BufferedImage对象。 5. 使用Java Graphics2D类创建一个新的BufferedImage对象,并将上传的图片绘制到新的图像上。 6. 使用Java Graphics2D类将水印图片绘制到新的图像上,并通过设置透明度来控制水印的透明度。 7. 最后,使用ImageIO类将新的BufferedImage对象换回MultipartFile类型的图片,并保存到指定的目录中。 以下是一个示例代码,实现了在Spring Boot中给MultipartFile类型的图片添加水印的功能: ```java import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @RestController public class WatermarkController { @PostMapping("/addWatermark") public String addWatermark(@RequestParam("file") MultipartFile file) { if (file.isEmpty()) { return "请选择要上传的文件"; } try { // 将上传的图片换成BufferedImage对象 BufferedImage image = ImageIO.read(file.getInputStream()); // 加载水印图片 BufferedImage watermark = ImageIO.read(new File("path/to/watermark.png")); // 创建新的BufferedImage对象 BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); // 创建Graphics2D对象,并绘制上传的图片 Graphics2D graphics2D = newImage.createGraphics(); graphics2D.drawImage(image, 0, 0, null); // 设置水印透明度 AlphaComposite alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f); graphics2D.setComposite(alphaComposite); // 绘制水印图片 graphics2D.drawImage(watermark, 0, 0, null); // 保存新的图片到指定目录 ImageIO.write(newImage, StringUtils.getFilenameExtension(file.getOriginalFilename()), new File("path/to/output.png")); graphics2D.dispose(); } catch (IOException e) { // 处理异常 } return "添加水印成功"; } } ``` 请注意,以上代码仅为示例,并未完整处理异常、文件路径等问题,实际应用中请根据需求进行适当的修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值