因一些合同、发票等单据需要生成pdf文档,并且审核通过后需给该pdf文档盖章,所以需要生成pdf的解决方案。
局限性
,如动态表格就不能友好的进行动态生成,生成了也是个绝对定位,假如文档后面还存在其它内容就会出现文本重合问题。https://blog.csdn.net/u014331138/article/details/108361728
生成的pdf文档,中文不显示,这个问题我找了很多文档,很多方法,最后只有微软雅黑'Microsoft YaHei'
才生效;因需求必须用宋体'SimSum'
,所以只能另找方法解决;
freemarker+html2pdf+font-asian
实现了pdf文档的生成,也满足了业务的需求,让我们继续往下看吧。
因itext
通过文本域进行替换值的局限性,所以通过freemarker
进行值的替换。
freemarker
进行数据的写入,生成一个替换好值的html文档,并保存到指定位置;resources
目录下建一个templates
将你所设计的ftl模板放入进去;resources
目录下建一个fonts
将你所需的字体放入进去;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<!-- itext7html转pdf --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>html2pdf</artifactId> <version>3.0.2</version> </dependency> <!-- 中文字体支持 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>font-asian</artifactId> <version>7.1.13</version> </dependency> <!--freemarker模板--> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> </dependency> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
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.property.TextAlignment; import com.itextpdf.layout.property.VerticalAlignment; import java.io.IOException; /** * 水印 */ public class WaterMarkEventHandler implements IEventHandler { /** * 水印内容 */ private String waterMarkContent; /** * 一页中有几列水印 */ private int waterMarkX; /** * 一页中每列有多少水印 */ private 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", false); } catch (IOException e) { e.printStackTrace(); } 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(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
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.property.TextAlignment; import java.io.IOException; /** * 页码 */ public class PageEventHandler implements IEventHandler { @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", false); } catch (IOException e) { e.printStackTrace(); } 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) + "页/共" + document.getNumberOfPages() + "页") .setFontSize(10) .setFont(pdfFont); canvas.showTextAligned(paragraph, x, y, TextAlignment.CENTER); canvas.close(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
import freemarker.template.Configuration; import freemarker.template.DefaultObjectWrapper; import freemarker.template.Template; import java.io.*; import java.util.Map; public class FreeMarkerUtils { private static Template getTemplate(String template_path, String templateFileName) { Configuration configuration = new Configuration(); Template template = null; try { configuration.setDirectoryForTemplateLoading(new File(template_path)); configuration.setObjectWrapper(new DefaultObjectWrapper()); configuration.setDefaultEncoding("UTF-8"); //设置编码格式 //模板文件 template = configuration.getTemplate(templateFileName + ".ftl"); } catch (IOException e) { e.printStackTrace(); } return template; } public static void genteratorFile(Map<String, Object> input, String template_path, String templateFileName, String savePath, String fileName) { Template template = getTemplate(template_path, templateFileName); File filePath = new File(savePath); if (!filePath.exists()) { filePath.mkdirs(); } String filename = savePath + "\\" + fileName; File file = new File(filename); if (!file.exists()) { file.delete(); } Writer writer = null; try { writer = new OutputStreamWriter(new FileOutputStream(filename), "UTF-8"); template.process(input, writer); } catch (Exception e) { e.printStackTrace(); } } public static String genterator(Map<String, Object> variables, String template_path, String templateFileName) throws Exception { Template template = getTemplate(template_path, templateFileName); StringWriter stringWriter = new StringWriter(); BufferedWriter writer = new BufferedWriter(stringWriter); template.setEncoding("UTF-8"); template.process(variables, writer); String htmlStr = stringWriter.toString(); writer.flush(); writer.close(); return htmlStr; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
import com.itextpdf.html2pdf.ConverterProperties; import com.itextpdf.html2pdf.HtmlConverter; import com.itextpdf.io.font.PdfEncodings; 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 com.jyxpackaging.ecp.microservices.common.core.utils.StringUtils; import lombok.extern.slf4j.Slf4j; import java.io.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Itext7转换工具类 */ @Slf4j public class HtmlToPdfUtils { /** * html转pdf * * @param inputStream 输入流 * @param waterMark 水印 * @param fontPath 字体路径,ttc后缀的字体需要添加<b>,0<b/> * @param outputStream 输出流 * @date : 2022/11/15 14:07 */ public static void convertToPdf(InputStream inputStream, String waterMark, String fontPath, OutputStream outputStream) throws IOException { PdfWriter pdfWriter = new PdfWriter(outputStream); PdfDocument pdfDocument = new PdfDocument(pdfWriter); // 设置为A4大小 pdfDocument.setDefaultPageSize(PageSize.A4); // 添加水印 pdfDocument.addEventHandler(PdfDocumentEvent.END_PAGE, new WaterMarkEventHandler(waterMark)); // 添加中文字体支持 ConverterProperties properties = new ConverterProperties(); FontProvider fontProvider = new FontProvider(); // 设置字体 /* PdfFont sysFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", false); fontProvider.addFont(sysFont.getFontProgram(), "UniGB-UCS2-H");*/ // 添加自定义字体,例如微软雅黑 if (StringUtils.isNotBlank(fontPath)) { PdfFont microsoft = PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H, false); fontProvider.addFont(microsoft.getFontProgram(), PdfEncodings.IDENTITY_H); } properties.setFontProvider(fontProvider); // 读取Html文件流,查找出当中的 或出现类似的符号空格字符 inputStream = readInputStrem(inputStream); if (inputStream != null) { // 生成pdf文档 HtmlConverter.convertToPdf(inputStream, pdfDocument, properties); pdfWriter.close(); pdfDocument.close(); return; } else { log.error("转换失败!"); } } /** * 读取HTML 流文件,并查询当中的 或类似符号直接替换为空格 * * @param inputStream * @return */ private static InputStream readInputStrem(InputStream inputStream) { // 定义一些特殊字符的正则表达式 如: String regEx_special = "\\&[a-zA-Z]{1,10};"; try { //<1>创建字节数组输出流,用来输出读取到的内容 ByteArrayOutputStream baos = new ByteArrayOutputStream(); //<2>创建缓存大小 byte[] buffer = new byte[1024]; // 1KB //每次读取到内容的长度 int len = -1; //<3>开始读取输入流中的内容 while ((len = inputStream.read(buffer)) != -1) { //当等于-1说明没有数据可以读取了 baos.write(buffer, 0, len); //把读取到的内容写到输出流中 } //<4> 把字节数组转换为字符串 String content = baos.toString(); //<5>关闭输入流和输出流 // inputStream.close(); baos.close(); // log.info("读取的内容:{}", content); // 判断HTML内容是否具有HTML的特殊字符标记 Pattern compile = Pattern.compile(regEx_special, Pattern.CASE_INSENSITIVE); Matcher matcher = compile.matcher(content); String replaceAll = matcher.replaceAll(""); // log.info("替换后的内容:{}", replaceAll); // 将字符串转化为输入流返回 InputStream stringStream = getStringStream(replaceAll); //<6>返回结果 return stringStream; } catch (Exception e) { e.printStackTrace(); log.error("错误信息:{}", e.getMessage()); return null; } } /** * 将一个字符串转化为输入流 * @param sInputString 字符串 * @return */ public static InputStream getStringStream(String sInputString) { if (sInputString != null && !sInputString.trim().equals("")) { try { ByteArrayInputStream tInputStringStream = new ByteArrayInputStream(sInputString.getBytes()); return tInputStringStream; } catch (Exception e) { e.printStackTrace(); } } return null; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.resource.ResourceUtil; import com.jyxpackaging.ecp.microservices.file.utils.DuizhangDomain; import com.jyxpackaging.ecp.microservices.file.utils.FreeMarkerUtils; import lombok.extern.slf4j.Slf4j; import java.io.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @Slf4j public class TestHtmlToPdf { // 模板路径 public final static String TEMP = "C:\\Users\\Administrator\\Desktop\\JYX_QYZT\\MicroServices\\base\\file\\src\\main\\resources\\templates\\"; /** * 模板所需的数据 * @return 数据 */ public static Map<String, Object> getContent() { // 从数据库中获取数据, 出于演示目的, 这里数据不从数据库获取, 而是直接写死 Map input = new HashMap(); input.put("采购单号", "Fuck you!采购单号~"); input.put("单据日期", "Fuck you!2"); input.put("工厂编号", "Fuck you!3"); input.put("供应商名称", "Fuck you!4"); input.put("工厂地址", "Fuck you!5"); input.put("供货方地址", "Fuck you!6"); input.put("公司电话", "Fuck you!7"); input.put("供应商电话", "Fuck you!8"); input.put("公司传真", "Fuck you!9"); input.put("供应商传真", "Fuck you!10"); input.put("公司联系人", "Fuck you!11"); input.put("供应商联系人及手机号", "Fuck you!12"); input.put("结算方式", "Fuck you!13"); input.put("运费", "Fuck you!14"); input.put("开机费", "Fuck you!15"); input.put("备注", "Fuck you!16"); input.put("合计金额", "Fuck you!17"); input.put("采购金额", "Fuck you!18"); // input.put("印章", "display:none"); input.put("印章", "display:block"); input.put("制表人", "Fuck you!20"); // 表格渲染数据 List<DuizhangDomain> dzList = new ArrayList<>(); dzList.add(new DuizhangDomain(1,"工单号", "物料料号", "物料名称","规格",1.0,"单位",16.5800,16.5800,16.5800, DateUtil.now(),"我的字体太长了怎么办")); dzList.add(new DuizhangDomain(2,"工单号2", "物料料号2", "物料名称2","规格2",2.0,"单位2",16.5800,16.5800,16.5800, DateUtil.now(),"怎么办怎么办怎么办怎么办")); dzList.add(new DuizhangDomain(3,"工单号3", "物料料号3", "物料名称3","规格3",2.0,"单位3",16.5800,16.5800,16.5800, DateUtil.now(),"怎么办怎么办怎么办怎么办~")); dzList.add(new DuizhangDomain(4,"工单号4", "物料料号4", "物料名称4","规格4",2.0,"单位4",16.5800,16.5800,16.5800, DateUtil.now(),"怎么办怎么办怎么办怎么办怎么办怎么办怎么办")); input.put("users", dzList); return input; } public static void main(String[] args) throws IOException { long startTime = System.currentTimeMillis(); // 指定模板渲染值并生成html文件至指定位置 FreeMarkerUtils.genteratorFile(getContent(), TEMP, "ProcurementContractTemplate", TEMP, "afterGeneration.html"); // 需转换的html文件名称 String htmlFile = "afterGeneration.html"; // 转换好pdf存储名称 String pdfFile = "result.pdf"; // 自定义水印 String waterMarkText = "JYX"; // 读取需转换的html文件 InputStream inputStream = new FileInputStream(TEMP + htmlFile); // 写出pdf存储位置 OutputStream outputStream = new FileOutputStream(TEMP + pdfFile); // 微软雅黑在windows系统里的位置如下,linux系统直接拷贝该文件放在linux目录下即可 // String fontPath = "src/main/resources/font/STHeiti Light.ttc,0"; String FONT_TTF_PATH = ResourceUtil.getResource("").getPath().replace("target/classes/", "").concat("src/main/resources/") + "fonts/simsun.ttc,0"; // 开始转换html生成pdf文档 HtmlToPdfUtils.convertToPdf(inputStream, waterMarkText, FONT_TTF_PATH, outputStream); log.info("转换结束,耗时:{}ms",System.currentTimeMillis()-startTime); } } |
from:https://www.cnblogs.com/lin02/p/itext.html