java html转pdf

项目需要实现自定义pdf模板,上网找了些资料实现了这个功能

参考文档:

https://my.oschina.net/960823/blog/1588166

https://my.oschina.net/u/3557716/blog/3023788/print

 

maven依赖:

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

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

PdfUtil.java

import java.io.FileOutputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;

import com.alibaba.fastjson.JSONObject;
import com.itextpdf.text.pdf.BaseFont;

/***
 * pdf工具类
 */
public class PdfUtil {
	/**
	 * 将HTML转成PD格式的文件。html文件的格式比较严格
	 * @param body body内容
	 * @param os 输出流
	 * @throws Exception
	 */
	public static void convertHtmlToPdf(String body, OutputStream os) throws Exception {
		body = filterHtml(body);
		ITextRenderer renderer = new ITextRenderer();
		String content = "<!DOCTYPE html [ <!ENTITY nbsp \"&#160;\">] >" + "<html>" + "	<head>"
				+ "		<style type=\"text/css\">" + "			body {" + "				font-family: SimSun;"
				+ "				background: none;" + "				margin-left: auto;"
				+ "				margin-right: auto;" + "			}" + "			p {"
				+ "				margin: 5px 0;" + "			}" + "		</style>" + "	</head>" + "	<body>" + body
				+ "	</body>" + "</html>" + "";

		renderer.setDocumentFromString(content);

		// step 3 解决中文支持
		ITextFontResolver fontResolver = renderer.getFontResolver();
		if ("linux".equals(getCurrentOperatingSystem())) {
			fontResolver.addFont("/usr/share/fonts/chiness/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
		} else {
			fontResolver.addFont("c:/Windows/Fonts/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
		}
		renderer.layout();
		renderer.createPDF(os);
		os.flush();
		os.close();
		System.out.println("create pdf done!!");
	}

	/***
	 * 获取系统环境
	 * @return
	 */
	public static String getCurrentOperatingSystem() {
		String os = System.getProperty("os.name").toLowerCase();
		System.out.println("---------当前操作系统是-----------" + os);
		return os;
	}

	/***
	 * 过滤html中的不规则文本
	 * @param content
	 */
	private static String filterHtml(String content) {
		return content.replace("<br>", "<br/>");
	}

	/***
	 * 将html转成pdf 并输出到outputstream中
	 * @param content  body正文
	 * @param valueMap 占位符解析的值map
	 * @param os 输出流
	 * @throws Exception
	 */
	public static void convertHtmlToPdfOutStream(String content, Map<String, Object> valueMap, OutputStream os)
			throws Exception {
		content = PlaceholderResolver.resolveByMap(content, valueMap);
		convertHtmlToPdfOutStream(content, os);
	}

	public static void convertHtmlToPdfOutStream(String content, OutputStream os) throws Exception {
		PdfUtil.convertHtmlToPdf(content, os);
	}
	
	/***
	 * 下载文件-测试方法
	 * @param os response的OutputStream
	 * @throws Exception
	 */
	public static void test(OutputStream os) throws Exception {
		String content = "<p style=\"text-align: center;\"><span style=\"font-size: 24px;\"><strong>测试模板</strong></span></p>\r\n" + 
				"		<p><span style=\"font-size: 14px;\">尊敬的<span style=\"font-size: 14px; color: rgb(255, 0, 0);\">${userName}</span>:</span></p>\r\n" + 
				"		<p style=\"text-indent: 2em;\"><span style=\"font-size: 14px;\">恭喜你中了100元大奖</span></p>\r\n" + 
				"		<p style=\"text-align: right; text-indent: 0em;\"><span style=\"font-size: 14px;\">${date}</span></p>\r\n" + 
				"		<p>&#8203;<br></p>";
		JSONObject map = new JSONObject();
		map.put("userName", "testAdmin");
		map.put("date", new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss").format(new Date()));
		content = PlaceholderResolver.resolveByMap(content, map);
		convertHtmlToPdfOutStream(content, os);
	}
	
	/***
	 * 生成本地pdf文件
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		String pdfFile = "d:/log/testoone2.pdf";
		String content = "<body style=\"cursor: text;\">\r\n"
				+ "		<p style=\"text-align: center;\"><span style=\"font-size: 24px;\">测试模板</span></p>\r\n"
				+ "		<p><span style=\"font-size: 14px;\">尊敬的<span style=\"font-size: 14px; color: rgb(255, 0, 0);\">${userName}</span>:</span></p>\r\n"
				+ "		<p><span style=\"font-size: 14px;\">&nbsp;&nbsp;&nbsp;&nbsp;&#8203;恭喜你中了100元大奖<br></span></p>\r\n"
				+ "		<p><span style=\"font-size: 14px;\"><br></span></p>\r\n"
				+ "		<p style=\"text-align: right;\"><span style=\"font-size: 14px;\">&#8203;2019年8月16日&nbsp;&nbsp;&nbsp;&nbsp;&#8203;</span></p>\r\n"
				+ "	</body>";
		JSONObject map = new JSONObject();
		map.put("userName", "testAdmin");
		content = PlaceholderResolver.resolveByMap(content, map);
		OutputStream os = new FileOutputStream(pdfFile);
		convertHtmlToPdfOutStream(content, os);
	}
}

PlaceholderResolver.java

import java.util.Map;
import java.util.Properties;
import java.util.function.Function;
import java.util.stream.Stream;

/**
 * 占位符解析器
 * @author meilin.huang
 * @version 1.0
 */
public class PlaceholderResolver {
    /**
     * 默认前缀占位符
     */
    public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";

    /**
     * 默认后缀占位符
     */
    public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";

    /**
     * 占位符前缀
     */
    private static String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;

    /**
     * 占位符后缀
     */
    private static String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;

    /**
     * 解析带有指定占位符的模板字符串,默认占位符为前缀:${  后缀:}<br/><br/>
     * 如:template = category:${}:product:${}<br/>
     * values = {"1", "2"}<br/>
     * 返回 category:1:product:2<br/>
     * @param content 要解析的带有占位符的模板字符串
     * @param values   按照模板占位符索引位置设置对应的值
     * @return
     */
    public static String resolve(String content, String... values) {
        int start = content.indexOf(placeholderPrefix);
        if (start == -1) {
            return content;
        }
        //值索引
        int valueIndex = 0;
        StringBuilder result = new StringBuilder(content);
        while (start != -1) {
            int end = result.indexOf(placeholderSuffix);
            String replaceContent = values[valueIndex++];
            result.replace(start, end + placeholderSuffix.length(), replaceContent);
            start = result.indexOf(placeholderPrefix, start + replaceContent.length());
        }
        return result.toString();
    }

    /**
     * 解析带有指定占位符的模板字符串,默认占位符为前缀:${  后缀:}<br/><br/>
     * 如:template = category:${}:product:${}<br/>
     * values = {"1", "2"}<br/>
     * 返回 category:1:product:2<br/>
     *
     * @param content 要解析的带有占位符的模板字符串
     * @param values   按照模板占位符索引位置设置对应的值
     * @return
     */
    public static String resolve(String content, Object[] values) {
        return resolve(content, Stream.of(values).map(String::valueOf).toArray(String[]::new));
    }

    /**
     * 根据替换规则来替换指定模板中的占位符值
     * @param content  要解析的字符串
     * @param rule  解析规则回调
     * @return
     */
    public static String resolveByRule(String content, Function<String, String> rule) {
        int start = content.indexOf(placeholderPrefix);
        if (start == -1) {
            return content;
        }
        StringBuilder result = new StringBuilder(content);
        while (start != -1) {
            int end = result.indexOf(placeholderSuffix, start);
            //获取占位符属性值,如${id}, 即获取id
            String placeholder = result.substring(start + placeholderPrefix.length(), end);
            //替换整个占位符内容,即将${id}值替换为替换规则回调中的内容
            String replaceContent = placeholder.trim().isEmpty() ? placeholder : rule.apply(placeholder);
            result.replace(start, end + placeholderSuffix.length(), replaceContent);
            start = result.indexOf(placeholderPrefix, start + replaceContent.length());
        }
        return result.toString();
    }

    /**
     * 替换模板中占位符内容,占位符的内容即为map key对应的值,key为占位符中的内容。<br/><br/>
     * 如:content = product:${id}:detail:${did}<br/>
     *    valueMap = id -> 1; pid -> 2<br/>
     *    经过解析返回 product:1:detail:2<br/>
     *
     * @param content  模板内容。
     * @param valueMap 值map
     * @return   替换完成后的字符串。
     */
    public static String resolveByMap(String content, final Map<String, Object> valueMap) {
        return resolveByRule(content, placeholderValue -> String.valueOf(valueMap.get(placeholderValue)));
    }

    /**
     * 根据properties文件替换占位符内容
     * @param content
     * @param properties
     * @return
     */
    public static String resolveByProperties(String content, final Properties properties) {
        return resolveByRule(content, placeholderValue -> properties.getProperty(placeholderValue));
    }
}

TestController.java   测试下载controller

import java.io.OutputStream;
import java.net.URLEncoder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/testHtmlToPdf")
public class TestController {
    
    @GetMapping("/getPdf")
    public void getPdf(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String fileName = URLEncoder.encode("模板.pdf", "UTF-8");
        // 设置response的Header
        response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
        
        OutputStream os = response.getOutputStream();
        PdfUtil.test(os);
        return;
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值