SpringBoot 集成 html2Pdf

news2024/11/25 20:33:15

一、概述:

        1. springboot如何生成pdf,接口可以预览可以下载

        2. vue下载通过bold如何下载

        3. 一些细节:页脚、页眉、水印、每一页得样式添加

二、直接上代码【主要是一个记录下次开发更快】

模板位置

1. 导入pom包

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>html2pdf</artifactId>
    <version>5.0.5</version>
</dependency>
<!-- 中文字体支持 -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>font-asian</artifactId>
    <version>7.2.1</version>
</dependency>

2. 写工具类

[1] 页眉工具类

import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.properties.TextAlignment;
import org.springframework.util.StringUtils;
import java.net.MalformedURLException;
import java.net.URL;

public class HeaderMarkerEventHandler implements IEventHandler {

    /**
     * pdf字体
     */
    private final PdfFont pdfFont;

    /**
     * 页眉显示
     */
    private final String title;

    /**
     * logo地址
     */
    private String logoUrl;

    public HeaderMarkerEventHandler(PdfFont pdfFont, String title) {
        this.pdfFont = pdfFont;
        this.title = title;
    }

    public HeaderMarkerEventHandler(PdfFont pdfFont, String title, String logoUrl) {
        this.pdfFont = pdfFont;
        this.title = title;
        this.logoUrl = logoUrl;
    }

    @Override
    public void handleEvent(Event event) {
        PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
        PdfDocument pdf = docEvent.getDocument();
        PdfPage page = docEvent.getPage();
        Rectangle pageSize = page.getPageSize();
        PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), pdf);
        Canvas canvas = new Canvas(pdfCanvas, pageSize);
        float x = pageSize.getRight() - 60;
        float y = pageSize.getTop() - 32;
        Paragraph p = new Paragraph(title)
                .setFontSize(9)
                .setFont(pdfFont);
        // 页眉字体显示得位置
        canvas.showTextAligned(p, x, y, TextAlignment.RIGHT);
        // 加载图片
        if (!StringUtils.isEmpty(logoUrl)) {
            try {
                URL url = new URL(logoUrl);
                Image logo = new Image(ImageDataFactory.create(url));
                logo.scaleAbsolute(100, 20);
                logo.setMarginLeft(30);
                logo.setMarginTop(15);
                canvas.add(logo);
            } catch (MalformedURLException e) {
                System.out.println("logo 无法解析: " + logoUrl);
            }
        }
        canvas.close();
    }
}

[2] 水印工具类


import com.itextpdf.kernel.colors.WebColors;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.VerticalAlignment;
import java.io.IOException;

/**
 * 生成 pdf 水印
 */
public class WaterMarkEventHandler implements IEventHandler {

    /**
     * 水印内容
     */
    private final String waterMarkContent;

    /**
     * 一页中有几列水印
     */
    private final int waterMarkX;

    /**
     * 一页中每列有多少水印
     */
    private final int waterMarkY;

    public WaterMarkEventHandler(String waterMarkContent) {
        this(waterMarkContent, 5, 5);
    }

    public WaterMarkEventHandler(String waterMarkContent, int waterMarkX, int waterMarkY) {
        this.waterMarkContent = waterMarkContent;
        this.waterMarkX = waterMarkX;
        this.waterMarkY = waterMarkY;
    }

    @Override
    public void handleEvent(Event event) {
        PdfDocumentEvent documentEvent = (PdfDocumentEvent) event;
        PdfDocument document = documentEvent.getDocument();
        PdfPage page = documentEvent.getPage();
        Rectangle pageSize = page.getPageSize();
        PdfFont pdfFont = null;
        try {
            pdfFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), document);
        Paragraph waterMark = new Paragraph(waterMarkContent).setOpacity(0.5f);
        Canvas canvas = new Canvas(pdfCanvas, pageSize)
                .setFontColor(WebColors.getRGBColor("lightgray"))
                .setFontSize(16)
                .setFont(pdfFont);

        for (int i = 0; i < waterMarkX; i++) {
            for (int j = 0; j < waterMarkY; j++) {
                canvas.showTextAligned(waterMark, (150 + i * 300), (160 + j * 150), document.getNumberOfPages(),
                        TextAlignment.CENTER, VerticalAlignment.BOTTOM, 120);
            }
        }
        canvas.close();
    }
}

[3]添加页脚工具类


import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.properties.TextAlignment;

/**
 * 生成 pdf 页码
 */
public class PageEventHandler implements IEventHandler {

    /**
     * pdf字体
     */
    private final PdfFont pdfFont;

    public PageEventHandler(PdfFont pdfFont) {
        this.pdfFont = pdfFont;
    }

    @Override
    public void handleEvent(Event event) {
        PdfDocumentEvent documentEvent = (PdfDocumentEvent) event;
        PdfDocument document = documentEvent.getDocument();
        PdfPage page = documentEvent.getPage();
        Rectangle pageSize = page.getPageSize();
        PdfCanvas pdfCanvas = new PdfCanvas(page.getLastContentStream(), page.getResources(), document);
        Canvas canvas = new Canvas(pdfCanvas, pageSize);
        float x = (pageSize.getLeft() + pageSize.getRight()) / 2;
        float y = pageSize.getBottom() + 15;
        Paragraph paragraph =
                new Paragraph(""+document.getPageNumber(page) )
                        .setFontSize(10)
                        .setFont(pdfFont);
        canvas.showTextAligned(paragraph, x, y, TextAlignment.CENTER);
        canvas.close();
    }
}

[4]工具类;重点说一下这个字体:

        1. 本文第一个图里面font得字体路径在Windows电脑:C:\Windows\Fonts;看中哪个字体就直接拷贝进去进会自动生成ttc文件;我拷贝得是宋体和微软雅黑,主要解决加粗

        2.前端 

font-family: "MicrosoftYaHei-Bold";

这个字体得名字日下图:

import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.font.FontProvider;
import org.apache.velocity.Template;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * PDF工具
 *
 * @author ppp
 * @date 2022/8/5
 */
public class Html2PdfUtil {

    static {
        Velocity.setProperty(RuntimeConstants.INPUT_ENCODING, StandardCharsets.UTF_8);
        Velocity.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
        Velocity.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
        Velocity.init();
    }

    /**
     * 据模板生成pfd格式文件
     *
     * @param context      上下文对象
     * @param template     pdf模板
     * @param outputStream 生成ofd文件输出流
     */
    public static void pdfFile(Context context, String template, OutputStream outputStream, String watermarkText) throws IOException {
        PdfWriter pdfWriter = null;
        PdfDocument pdfDocument = null;
        StringWriter writer = null;
        try {
           pdfWriter = new PdfWriter(outputStream);
            pdfDocument = new PdfDocument(pdfWriter);
            PageSize pageSize = new PageSize(842.0F,595.0F);
            pdfDocument.setDefaultPageSize(pageSize);
            ConverterProperties properties = new ConverterProperties();
            // 添加字体
            FontProvider fontProvider = new FontProvider();
            // 字体设置,解决中文不显示问题
            PdfFont sysFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H");
            fontProvider.addFont(sysFont.getFontProgram(), "UniGB-UCS2-H");
//           添加宋体,html中使用SimSun,不指定则默认为第一个引入的字体
            sysFont = PdfFontFactory.createFont("font/simsun.ttc,0", PdfEncodings.IDENTITY_H);
            fontProvider.addFont(sysFont.getFontProgram(), PdfEncodings.IDENTITY_H);
            // 添加微软雅黑,html中使用MicrosoftYaHei
            sysFont = PdfFontFactory.createFont("font/msyh.ttc,0", PdfEncodings.IDENTITY_H);
            fontProvider.addFont(sysFont.getFontProgram(), PdfEncodings.IDENTITY_H);
            // 添加微软雅黑粗体,html中使用MicrosoftYaHei-Bold
            sysFont = PdfFontFactory.createFont("font/msyhbd.ttc,0", PdfEncodings.IDENTITY_H);
            fontProvider.addFont(sysFont.getFontProgram(), PdfEncodings.IDENTITY_H);
            // 处理微软雅黑及粗体描述符错乱问题
            processYaHeiFontDescriptor(fontProvider);
            properties.setFontProvider(fontProvider);
            // 添加页眉
//            pdfDocument.addEventHandler(PdfDocumentEvent.START_PAGE, new HeaderMarkerEventHandler(sysFont, "学校"));
            // 添加水印
            pdfDocument.addEventHandler(PdfDocumentEvent.INSERT_PAGE, new WaterMarkEventHandler(watermarkText));
            // 添加页脚
            pdfDocument.addEventHandler(PdfDocumentEvent.END_PAGE, new PageEventHandler(sysFont));
            // 读取html模板和赋值
            Template pfdTemplate = Velocity.getTemplate(template, "UTF-8");
            writer = new StringWriter();
            pfdTemplate.merge(context, writer);
            // 构建带有样式的 HTML 字符串
            StringBuilder htmlWithStyles = new StringBuilder();
            htmlWithStyles.append("<html><head><style>");
            // 如果有边距参数,则添加到样式中
            htmlWithStyles.append("@page { ");
            htmlWithStyles.append("margin-bottom: ").append(30).append("mm; ");
            htmlWithStyles.append("}");
            htmlWithStyles.append("</style></head><body>").append("</body></html>");
            // html转换PDF
            HtmlConverter.convertToPdf(writer.toString(), pdfDocument, properties);
        } catch (Exception e) {
            throw new RuntimeException("PFD文件生成失败", e);
        }finally {
            pdfDocument.close();
            writer.close();
            pdfWriter.close();
        }
    }
}

3.html模板,里面那个分页,就是新开一页,估计有点问题

        【1】、td 换行得自己加

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
</head>
<body>
<div class="contianer">
    <div>
        <div class="fourth_title">
            <h4>2-2 专业群建设总目标</h4>
        </div>
        <div class="third_con">
            <p  class="pFontCon">
                $!{xxxxmb.zyqjszmb}

            </p>
        </div>
    </div>

    <div>
        #foreach($html in $canVasHtml)
            <div class="page-break">
                $html
            </div>
        #end

    </div>

</div>
</body>
<style>
    h1,h2 {
        font-style: italic;
        font-family: "MicrosoftYaHei-Bold";
        text-align: center;
        font-weight: bold; /* 加粗文本 */
    }
    td{
        white-space:normal;
        word-wrap:break-word;
        word-break:break-all;
    }
    @media print {
        .page-break {
            page-break-before: always; /* 在元素之前插入分页 */
        }
    }
/* 每次新开一个分页 */
    .page-break {
        page-break-before: always; /* 在元素之前插入分页 */
    }
</style
</html>


4. Service 只留了一部分代码

@Override
public void downloadAchievementsByPdf( HttpServletResponse response, HttpServletRequest request) throws IOException {
    OutputStream outputStream = null;
    try {
        response.reset();
        String fileName = schooleInfo.getXxmc();
//      这个是直接下载
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".pdf", "utf-8"));
        // 这个是预览,访问接口页面打开得是一个FPD预览得页面
//       response.setContentType("application/pdf");
//       response.addHeader("Content-Disposition", "inline; filename=" + fileName);
        VelocityContext context = new VelocityContext();
        setBaseInfo(context, schooleInfo);
        context.put("zyqxxList", zyqjbxxTS);
        context.put("xxxxmb", zyqjbxxTInfoOne);
        // 获取指标数据
        List<Map<String, Object>> maps = (List<Map<String, Object>>) treeWithCanvas(schoolBookId, isTask, taskId, schoolId).get("data");
        List<String> canVasHtml = PdfTemplate.getCanVasHtml(maps);
        context.put("canVasHtml", canVasHtml);
        outputStream = response.getOutputStream();
        Html2PdfUtil.pdfFile(context, "templates/jxzpbPdf.html", outputStream, schooleInfo.getXxmc());
    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        outputStream.close();
    }
}

private void setBaseInfo(VelocityContext context, XxbcxxT schooleInfo) {
    // 1. 查询学校基本信息
    context.put("xmdw", schooleInfo.getXxmc());
    context.put("xmmc", schooleInfo.getXmmc());
    context.put("tbrq", DateUtils.dateTimeNow(DateUtils.YYYY_MM_DD));
    context.put("jbdw", schooleInfo.getJbdw());
    context.put("szsf", schooleInfo.getSfbm() == null ? "" : ProvinceJson.getProName(schooleInfo.getSfbm()));
}

5. VUE代码

  return request(url, {
    ...params,
    method: 'GET',
    responseType: 'blob'
  }).then((data) => {
    const aLink = document.createElement('a');
    const blob =  new Blob([data],{type: 'application/pdf'});  
    aLink.style.display = 'none';
    aLink.href = URL.createObjectURL(blob);
    aLink.setAttribute('download', fileName); 
    document.body.appendChild(aLink);
    aLink.click();
    URL.revokeObjectURL(aLink.href); // 清除引用
    document.body.removeChild(aLink);
  });

三、最后还有一个加粗得问题。思路就是需要引入字体文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2247479.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java实现小程序接口返回Base64图片

文章目录 引言I java 接口返回Base64图片接口设计获取验证码图片-base64字符串获取验证码图片-二进制流arraybufferII 小程序端代码过期代码: 显示文件流图片(arraybuffer)知识扩展:微信小程序下载后端返回的文件流引言 场景: 图形验证码 背景: 接口返回arraybuffer的格式…

Pytorch自定义算子反向传播

文章目录 自定义一个线性函数算子如何实现反向传播 有关 自定义算子的实现前面已经提到&#xff0c;可以参考。本文讲述自定义算子如何前向推理反向传播进行模型训练。 自定义一个线性函数算子 线性函数 Y X W T B Y XW^T B YXWTB 定义输入M 个X变量&#xff0c;输出N个…

ajax (一)

什么是 AJAX [ˈeɪdʒks] &#xff1f; 概念&#xff1a;AJAX是浏览器与服务器进行 数据通信 的技术&#xff0c;动态数据交互 怎么用AJAX? 1. 先使用 axios [k‘sio ʊ s] 库&#xff0c; 与服务器进行 数据通信 ⚫ 基于 XMLHttpRequest 封装、代码简单、月下载量在 1…

URL在线编码解码- 加菲工具

URL在线编码解码 打开网站 加菲工具 选择“URL编码解码” 输入需要编码/解码的内容&#xff0c;点击“编码”/“解码”按钮 编码&#xff1a; 解码&#xff1a; 复制已经编码/解码后的内容。

魔众题库系统 v10.0.0 客服条、题目导入、考试导航、日志一大批更新

魔众题库系统基于PHP开发&#xff0c;可以用于题库管理和试卷生成软件&#xff0c;拥有极简界面和强大的功能&#xff0c;用户遍及全国各行各业。 魔众题库系统发布v10.0.0版本&#xff0c;新功能和Bug修复累计30项&#xff0c;客服条、题目导入、考试导航、日志一大批更新。 …

深入解析 EasyExcel 组件原理与应用

✨深入解析 EasyExcel 组件原理与应用✨ 官方&#xff1a;EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网 在日常的 Java 开发工作中&#xff0c;处理 Excel 文件的导入导出是极为常见的需求。 今天&#xff0c;咱们就一起来深入了解一款非常实用的操作 Exce…

本地部署 MaskGCT

本地部署 MaskGCT 0. 更新系统和安装依赖项1. 克隆代码2. 创建虚拟环境3. 安装依赖模块4. 运行 MaskGCT5. 访问 MaskGCT 0. 更新系统和安装依赖项 sudo apt update sudo apt install espeak-ng1. 克隆代码 git clone https://github.com/engchina/learn-maskgct.git; cd lear…

线程控制方法之wait和sleep的区别

线程控制方法之wait和sleep的区别 wait()和sleep()都是Java线程控制方法&#xff0c;但存在明显区别&#xff1a; 所属与调用&#xff1a;wait()属Object类&#xff0c;需synchronized调用&#xff1b;sleep()属Thread类&#xff0c;可随意调用。锁处理&#xff1a;wait()释放…

Fakelocation Server服务器/专业版 Centos7

前言:需要Centos7系统 Fakelocation开源文件系统需求 Centos7 | Fakelocation | 任务一 更新Centos7 &#xff08;安装下载不再赘述&#xff09; sudo yum makecache fastsudo yum update -ysudo yum install -y kernelsudo reboot//如果遇到错误提示为 Another app is curre…

探索 RocketMQ:企业级消息中间件的选择与应用

一、关于RocketMQ RocketMQ 是一个高性能、高可靠、可扩展的分布式消息中间件&#xff0c;它是由阿里巴巴开发并贡献给 Apache 软件基金会的一个开源项目。RocketMQ 主要用于处理大规模、高吞吐量、低延迟的消息传递&#xff0c;它是一个轻量级的、功能强大的消息队列系统&…

基于信创环境的信息化系统运行监控及运维需求及策略

随着信息技术的快速发展和国家对信息安全的日益重视&#xff0c;信创环境&#xff08;信息技术应用创新环境&#xff09;的建设已成为行业发展的重要趋势。本指南旨在为运维团队在基于信创环境的系统建设及运维过程中提供参考&#xff0c;确保项目顺利实施并满足各项技术指标和…

初学 flutter 问题记录

windows搭建flutter运行环境 一、运行 flutter doctor遇到的问题 Xcmdline-tools component is missingRun path/to/sdkmanager --install "cmdline-tools;latest"See https://developer.android.com/studio/command-line for more details.1&#xff09;cmdline-to…

【虚拟机】VMWare的CentOS虚拟机断电或强制关机出现问题

VMware 虚拟机因为笔记本突然断电故障了&#xff0c;开机提示“Entering emergency mode. Exit the shell to continue.”&#xff0c;如下图所示&#xff1a; 解决方法&#xff1a;输入命令&#xff1a; xfs_repair -v -L /dev/dm-0 注&#xff1a;报 no such file or direct…

设计模式:6、装饰模式(包装器)

目录 0、定义 1、装饰模式包含的四种角色 2、装饰模式的UML类图 3、示例代码 0、定义 动态地给对象添加一些额外的职责。就功能来说装饰模式相比生成子类更为灵活。 1、装饰模式包含的四种角色 抽象组件&#xff08;Component&#xff09;&#xff1a;抽象组件是一个抽象…

Java开发经验——Spring Test 常见错误

摘要 本文详细介绍了Java开发中Spring Test的常见错误和解决方案。文章首先概述了Spring中进行单元测试的多种方法&#xff0c;包括使用JUnit和Spring Boot Test进行集成测试&#xff0c;以及Mockito进行单元测试。接着&#xff0c;文章分析了Spring资源文件扫描不到的问题&am…

Java基于Spring Boot框架的房屋租赁系统,附源码

博主介绍&#xff1a;✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…

单片机_简单AI模型训练与部署__从0到0.9

IDE&#xff1a; CLion MCU&#xff1a; STM32F407VET6 一、导向 以求知为导向&#xff0c;从问题到寻求问题解决的方法&#xff0c;以兴趣驱动学习。 虽从0&#xff0c;但不到1&#xff0c;剩下的那一小步将由你迈出。本篇主要目的是体验完整的一次简单AI模型部署流程&#x…

Java-08 深入浅出 MyBatis - 多对多模型 SqlMapConfig 与 Mapper 详细讲解测试

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…

IDEA使用tips(LTS✍)

一、查找项目中某个外部库依赖类的pom来源 1、显示图 2、导出Maven 项目依赖的可视化输出文件 3、点击要查找的目标类&#xff0c;项目中定位后复制依赖名称 4、在导出的依赖的可视化文件中搜索查找 5、综上得到&#xff0c;Around类来自于pom中的spring-boot-starter-aop:jar…

【LLM训练系列02】如何找到一个大模型Lora的target_modules

方法1&#xff1a;观察attention中的线性层 import numpy as np import pandas as pd from peft import PeftModel import torch import torch.nn.functional as F from torch import Tensor from transformers import AutoTokenizer, AutoModel, BitsAndBytesConfig from typ…