当前位置: 首页 > article >正文

springboot导出pdf,解决中文问题

引入包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>html2pdf</artifactId>
    <version>5.0.5</version>
</dependency>

 去windows目录下 获取字体   C:\Windows\Fonts    simsunb.ttf、或者下载.ttc

@GetMapping("/download")
    @ApiOperation("下载")
    public void download(AlgorithmRouteDownloadDto downloadDto, HttpServletResponse response) {

        try (OutputStream os = response.getOutputStream()) {
            response.reset();
            response.setContentType("application/octet-stream");
            response.setHeader("Content-disposition", "attachment;filename=user_pdf_" + System.currentTimeMillis() + ".pdf");

            //获取指定的路由定义
 
            List<AlgorithmRouteDownloadVo> routeApis = this.algorithmRouteMapper.selectJoinList(AlgorithmRouteDownloadVo.class, queryWrapper);
     


            HashMap<String, Object> mapData = Maps.newHashMap();
            mapData.put("routeApis", routeApis);
            String templateContent = HtmlUtils.getTemplateContent("index.ftl", mapData);
            ByteArrayOutputStream byteArrayOutputStream = HtmlUtils.html2Pdf(templateContent);
            os.write(byteArrayOutputStream.toByteArray());
        } catch (Exception e) {
 
            log.error("error occurs when downloading file",e);
        }
    }

 

package com.ev.edge.utils;

import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.layout.font.FontProvider;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class HtmlUtils {

    /**
     * @return
     * @throws Exception
     */
    public static String getFileDirectory(String fileName) {
        ClassLoader classLoader = HtmlUtils.class.getClassLoader();
        URL resource = classLoader.getResource(fileName);
        try {
            return Objects.requireNonNull(resource).toURI().getPath();
        } catch (URISyntaxException e) {
            log.error("获取模板文件夹失败,{}", e);
        }
        return null;
    }

    /**
     * 获取模板内容
     *
     * @param templateName 模板文件名
     * @param paramMap     模板参数
     * @return
     * @throws Exception
     */
    public static String getTemplateContent(String templateName, Map<String, Object> paramMap) throws Exception {
        Configuration config = SpringUtil.getBean(FreeMarkerConfigurer.class).getConfiguration();
        config.setDefaultEncoding("UTF-8");
        config.setEncoding(Locale.CHINA, "UTF-8");
        Template template = config.getTemplate(templateName, "UTF-8");
        return FreeMarkerTemplateUtils.processTemplateIntoString(template, paramMap);
    }

    /**
     * HTML 转 PDF
     *
     * @param content html内容
     * @param outPath 输出pdf路径
     * @return 是否创建成功
     */
    public static boolean html2Pdf(String content, String outPath) {
        try {
            ConverterProperties converterProperties = new ConverterProperties();
            converterProperties.setCharset("UTF-8");
            FontProvider fontProvider = new FontProvider();
            fontProvider.addSystemFonts();
            converterProperties.setFontProvider(fontProvider);
            HtmlConverter.convertToPdf(content, new FileOutputStream(outPath), converterProperties);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("生成模板内容失败,{}", e);
            return false;
        }
        return true;
    }

    /**
     * HTML 转 PDF
     *
     * @param content html内容
     * @return PDF字节数组
     */
    public static ByteArrayOutputStream html2Pdf(String content) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            ConverterProperties converterProperties = new ConverterProperties();
            converterProperties.setCharset("UTF-8");
            FontProvider fontProvider = new FontProvider();

            PdfFont fftFont = PdfFontFactory.createFont("fonts/simsunb.ttf", PdfEncodings.IDENTITY_H);
            PdfFont ttcFont = PdfFontFactory.createTtcFont("fonts/simsun.ttc", 1, PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED, true);
            boolean ttcFontResult = fontProvider.addFont(ttcFont.getFontProgram());
            boolean fftFontResult = fontProvider.addFont(fftFont.getFontProgram());
            log.info("html2Pdf add fount ttf:{} ttc:{}", fftFontResult, ttcFontResult);
            fontProvider.addSystemFonts();
            converterProperties.setFontProvider(fontProvider);
            HtmlConverter.convertToPdf(content, outputStream, converterProperties);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("生成 PDF 失败,{}", e);
        }
        return outputStream;
    }

    public static List<Map<String, Object>> convertJsonToListMap(String json) {
        if (StringUtils.isBlank(json)) {
            return Collections.emptyList();
        }
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            // 将 JSON 字符串转换为 List<Map<String, Object>> 类型
            List<Map<String, Object>> list = objectMapper.readValue(json, objectMapper.getTypeFactory().constructCollectionType(List.class, Map.class));

            // 将 List<Map<String, Object>> 转换为 List<Map<String, Object>>,并排除 'id' 和 'mapping' 字段
            return list.stream()
                    .map(map -> filterMap(map))
                    .collect(Collectors.toList());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static Map<String, Object> filterMap(Map<String, Object> map) {
        return map.entrySet().stream()
                .filter(entry -> !"id".equals(entry.getKey()) && !"mapping".equals(entry.getKey()))
                .collect(Collectors.toMap(
                        Map.Entry::getKey, // 保持 key 不变
                        entry -> {
                            if (entry.getValue() instanceof List) {
                                // 处理 children 字段
                                @SuppressWarnings("unchecked")
                                List<Map<String, Object>> childrenList = (List<Map<String, Object>>) entry.getValue();
                                return childrenList.stream()
                                        .map(child -> filterMap(child))
                                        .collect(Collectors.toList());
                            } else if (entry.getValue() instanceof Map) {
                                // 递归处理嵌套的 Map
                                return filterMap((Map<String, Object>) entry.getValue());
                            } else {
                                // 其他 value 转为 String
                                return entry.getValue().toString();
                            }
                        }
                ));
    }
}

 

<!DOCTYPE html>
<html>
    <head>
       <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
        <title></title>
		<style>
			.main{
				width: 60%;
				margin: 0 auto;
				padding-bottom: 30px;
			}
			table {
			    width: 100%;
			    font-size: 14px;
			    margin:10px auto;
			    border-collapse: collapse;
			}
			th, td {
			    border: 1px solid #000;
			    padding: 2px 5px;
			    text-align: center;
			}
			th {
			    font-weight: bold;
			}
			.thFirst{
				width: 30%;
			}
			.sizeWeight{
				font-weight: bold;
			}
		</style>
    </head>
    <body>
        <#list routeApis as routeApi>
		<div class="main">
			<h3>${routeApi?index+1}. 	<#if routeApi.algoName??> ${routeApi.algoName}  <#else> ${routeApi.remark!""} </#if></h3>
			<p><span class="sizeWeight">路由地址:</span>${routeApi.routePath}</p>
			<p><span class="sizeWeight">请求方式:</span>${routeApi.routeMethod}</p>
			<p><span class="sizeWeight">描述:</span> <#if routeApi.algoName??>  ${routeApi.algoName} <#else>  ${routeApi.remark!""} </#if>  </p>
			<h4>请求参数:</h4>
			
			<table>
				<thead>
					<tr>
						<th class="thFirst">参数</th>
						<th>说明</th>					
					</tr>
				</thead>
				<tbody>
				<#if routeApi.reqParamList??>
                    <#list routeApi.reqParamList as reqParam>
                        <#list reqParam?keys as key>
                              <#assign value = reqParam[key]>
                              <#if key != "children">
                                <tr>
                                    <td>${key}</td>
                                    <td>${value!}</td>
                                </tr>
                              </#if>
                              <#if key == "children">
                                  <#if value??>
                                      <#list value as childValue>
                                         <#list childValue?keys as key1>
                                             <#assign value1 = childValue[key1]>
                                             <tr>
                                             <td>-${key1}</td>
                                             <td>${value1}</td>
                                             </tr>
                                         </#list>
                                      </#list>
                                  </#if>
                              </#if>
                        </#list>
                    </#list>
                <#else>
                    <tr>
                        <td colspan="2">无请求参数</td>
                    </tr>
                </#if>
				</tbody>
			</table>
	
			<h4>返回参数:</h4>
			<table>
				<thead>
					<tr>
						<th class="thFirst">参数</th>
						<th>说明</th>					
					</tr>
				</thead>
				<tbody>
                <#if routeApi.resParamList??>
                     <#list routeApi.resParamList as resParam>
                        <#list resParam?keys as key>
                                <#assign value = resParam[key]>
                                <#if key != "children">
                                <tr>
                                    <td>${key}</td>
                                    <td>${value!}</td>
                                </tr>
                                </#if>
                                 <#if key == "children">
                                     <#if value??>
                                     <#list value as childValue>
                                        <#list childValue?keys as key1>
                                            <#assign value1 = childValue[key1]>
                                            <tr>
                                            <td>-${key1}</td>
                                            <td>${value1}</td>
                                            </tr>
                                        </#list>
                                     </#list>
                                     </#if>
                                 </#if>
                        </#list>
                     </#list>
                <#else>
                    <tr>
                        <td colspan="2">无请求参数</td>
                    </tr>
                </#if>
				</tbody>
			</table>
			
			<h4>响应码:</h4>
			<table>
				<thead>
					<tr>
						<th class="thFirst">响应码</th>
						<th>说明</th>					
					</tr>
				</thead>
				<tbody>
               <#if routeApi.resCodeList??>
                   <#list routeApi.resCodeList as resCode>
                       <#list resCode?keys as key>
                           <#assign value = resCode[key]>

                           <#if key != "children">
                               <tr>
                                   <td>${key}</td>
                                   <td>${value!}</td>
                               </tr>
                           </#if>
                          <#if key == "children">
                               <#if value??>
                               <#list value as childValue>
                                  <#list childValue?keys as key1>
                                      <#assign value1 = childValue[key1]>
                                      <tr>
                                      <td>-${key1}</td>
                                      <td>${value1}</td>
                                      </tr>
                                  </#list>
                               </#list>
                               </#if>
                           </#if>
                       </#list>
                   </#list>
               <#else>
                   <tr>
                       <td colspan="2">无响应码</td>
                   </tr>
               </#if>
				</tbody>
			</table>
		</div>  
        </#list>

    </body>
</html>

 


http://www.kler.cn/a/374763.html

相关文章:

  • 运动控制卡网络通讯的心跳检测之C#上位机编程
  • 简单了解函数递归
  • 突发!!!GitLab停止为中国大陆、港澳地区提供服务,60天内需迁移账号否则将被删除
  • Linux 常见用例汇总
  • 干货分享:ISO 20000认证的适用范围、认证资料清单、认证流程等问题详解
  • 探寻快速排序的局限性及其优化策略
  • 在 Android 设备上部署一个 LLM(大语言模型)并通过 Binder 通信提供服务
  • Java 字符流详解
  • Zoho Desk系统解锁工单自动化 分配效率翻倍
  • ffmpeg拉流分段存储到文件-笔记
  • SC5120家庭总线收发器可pin to pin兼容MAX22088
  • WAF+AI结合,雷池社区版的强大防守能力
  • scp免密传输教程
  • QT 跨平台优势独特,效果实例设计精彩呈现
  • 【Redis】内存淘汰策略
  • sqoop Oracle to hive出现 Error Msg = ORA-00933: SQL 命令未正确结束
  • 3周岁孤独症儿童:治愈的希望还是幻想?
  • 一文读懂 HTTP Cookies
  • Python批量查找包含多个关键词的PDF文件
  • CSP/信奥赛C++刷题训练:经典差分例题(2):洛谷P9904 :Mieszanie kolorów
  • TS:如何推导函数类型
  • 探索Unity:从游戏引擎到元宇宙体验,聚焦内容创作
  • 双十一大促有哪些值得入手的产品?这五款产品实用又划算!
  • Three.js 粒子系统教程构建炫酷的 3D 粒子效果
  • 蘑菇书(EasyRL)学习笔记(2)
  • 如何在算家云搭建GFP-GAN(图像生成)