java生成PDF的几种方法

java生成PDF的几种方法问题场景总结一下用 java 生成 PDF 的方法 A itext PdfStamperpd 俗称抠模板 B itext Documentdocu 正常代码撰写 C wkhtmltopdf 使用工具 分析比较方法优点缺点 A 代码简单模板要先提供 且字段长度固定 不灵活 B 模板可根据代码调整 但样式不如 C 灵活要维护的后台代码较多 C

问题场景

分析比较

方法 优点 缺点
A 代码简单 模板要先提供,且字段长度固定、不灵活
B 模板可根据代码调整、但样式不如C灵活 要维护的后台代码较多
C 模板样式可根据前端随意调整 要维护的前台代码较多

用到的资源

举例:

/ * 抠模板 * @throws Exception */ public void createAllPdf() throws Exception { //填充创建pdf PdfReader reader = null; PdfStamper stamp = null; try { reader = new PdfReader("E:/module.pdf"); SimpleDateFormat simp = new SimpleDateFormat("yyyy-MM-dd"); String times = simp.format(new Date()).trim(); //创建生成报告名称 String root = ServletActionContext.getRequest().getRealPath("/upload") + File.separator; if (!new File(root).exists()) new File(root).mkdirs(); File deskFile = new File(root, times + ".pdf"); stamp = new PdfStamper(reader, new FileOutputStream(deskFile)); //取出报表模板中的所有字段 AcroFields form = stamp.getAcroFields(); // 填充数据 form.setField("name", "zhangsan"); form.setField("sex", "男"); form.setField("age", "15"); //报告生成日期 SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd"); String generationdate = dateformat.format(new Date()); form.setField("generationdate", generationdate); stamp.setFormFlattening(true); } catch (Exception e) { e.printStackTrace(); } finally { if (stamp != null) { stamp.close(); } if (reader != null) { reader.close(); } } } 
package itext; import com.itextpdf.text.*; import com.itextpdf.text.pdf.*; import java.io.FileNotFoundException; import java.io.FileOutputStream; / * Created on 2017/5/16 * Author: youxingyang. */ public class TableAndTitle { / * @param args */ public static void main(String[] args) throws Exception { String fileName = "tableAndTitle.pdf"; TableAndTitle.test(fileName); } private static void test(String fileName) { Document document = new Document(); try { PdfWriter.getInstance(document, new FileOutputStream(fileName)); document.open(); PdfPTable table = new PdfPTable(1); table.setKeepTogether(true); table.setSplitLate(false); PdfPTable table1 = new PdfPTable(1); PdfPCell cell0 = new PdfPCell(); Paragraph p = new Paragraph("table title sample"); p.setAlignment(1); p.setSpacingBefore(15f); cell0.setHorizontalAlignment(PdfPCell.ALIGN_CENTER); cell0.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);//然并卵 cell0.setPaddingTop(-2f);//把字垂直居中 cell0.setPaddingBottom(8f);//把字垂直居中 cell0.addElement(p); cell0.setBorder(0); table1.addCell(cell0); PdfPTable pTable = new PdfPTable(table1); document.add(pTable); PdfPTable table2 = new PdfPTable(2); float border = 1.5f; for (int a = 0; a < 20; a++) { PdfPCell cell = new PdfPCell(); Paragraph pp; if (a == 0 || a == 1) { pp = str2ParaByTwoFont("tableTitle" + (a + 1), 9f, BaseColor.BLACK, Font.BOLD); //小五 加粗 cell.setBorderWidthBottom(border); cell.setBorderWidthTop(border); } else { if (a == 18 || a == 19) { cell.setBorderWidthTop(0); cell.setBorderWidthBottom(border); } else { cell.setBorderWidthBottom(0); cell.setBorderWidthTop(0); } pp = str2ParaByTwoFont("tableContent" + (a - 1), 9f, BaseColor.BLACK); //小五 } //设置间隔的背景色 if ((a + 1) % 2 == 0) { if (((a + 1) / 2) % 2 == 1) { cell.setBackgroundColor(new BaseColor(128, 128, 255)); } else { cell.setBackgroundColor(new BaseColor(128, 255, 255)); } } else { if (((a + 1) / 2) % 2 == 1) { cell.setBackgroundColor(new BaseColor(128, 255, 255)); } else { cell.setBackgroundColor(new BaseColor(128, 128, 255)); } } pp.setAlignment(1); cell.setBorderWidthLeft(0); cell.setBorderWidthRight(0); cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER); cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);//然并卵 cell.setPaddingTop(-2f);//把字垂直居中 cell.setPaddingBottom(8f);//把字垂直居中 cell.addElement(pp); table2.addCell(cell); } PdfPCell c1 = new PdfPCell(); c1.setBorder(0); c1.addElement(table1); PdfPCell c2 = new PdfPCell(); c2.setBorder(0); c2.addElement(table2); table.addCell(c1); table.addCell(c2); document.add(table); document.close(); } catch (DocumentException | FileNotFoundException e) { e.printStackTrace(); } } / * 两种字体显示文字 * * @param cont * @param size * @param color * @return */ private static Paragraph str2ParaByTwoFont(String cont, float size, BaseColor color) { Paragraph res = new Paragraph(); FontSelector selector = new FontSelector(); //非汉字字体颜色 Font f1 = FontFactory.getFont(FontFactory.TIMES_ROMAN, size); f1.setColor(color); //汉字字体颜色 Font f2 = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED, size); f2.setColor(color); selector.addFont(f1); selector.addFont(f2); Phrase ph = selector.process(cont); res.add(ph); return res; } / * 两种字体显示文字 * * @param cont * @param size * @param color * @param bold * @return */ private static Paragraph str2ParaByTwoFont(String cont, float size, BaseColor color, int bold) { Paragraph res = new Paragraph(); FontSelector selector = new FontSelector(); //非汉字字体颜色 Font f1 = FontFactory.getFont(FontFactory.TIMES_ROMAN, size); f1.setColor(color); f1.setStyle(bold); //汉字字体颜色 Font f2 = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED, size); f2.setColor(color); f2.setStyle(bold); selector.addFont(f1); selector.addFont(f2); Phrase ph = selector.process(cont); res.add(ph); return res; } } 
/ * 多线程生成报告 * @param map 样本及公司对应集合 * @param productMap 样本及产品对应集合 * @param sampleList 样本列表集合 * @param storeDir 报告存储路径 * @param url 源转换的页面url前缀 * @param pyPre python脚本存放的位置 * @param uuid 本地任务唯一标识 * @param storePrefix url参数文件前缀 * @return */ private static int createMul(Map 
  
    map, Map 
   
     productMap, List 
    
      sampleList, String storeDir, String url, String pyPre, String uuid, String storePrefix) { String date = DateUtil.date2Str(new Date()); StringBuilder pathTemp = new StringBuilder(""); String companyId; String productCode; String cmd; int sum = 0; Map 
     
       sampleMap = new LinkedHashMap<>(sampleList.size()); String paraFileName; try { String path; for (String sampleCode : sampleList) { companyId = map.get(sampleCode); productCode = productMap.get(sampleCode); pathTemp.append(storeDir).append(date).append("-").append(uuid).append(File.separator).append(companyId).append(File.separator).append(productCode); path = pathTemp.toString(); pathTemp.setLength(0); File file = new File(path); if (!file.exists()) { file.mkdirs(); } path += File.separator + sampleCode + "-" + productCode + ".pdf"; path = path.replace("\\", "/"); sampleMap.put(sampleCode, path); } paraFileName = storePrefix + DateUtil.date2Str(new Date()) + "-" + EncryUtil.getUUID() + ".txt"; boolean success = writeMapFile(sampleMap, paraFileName); if (success) { log.info("多线程生成报告参数: url: {}, paraFileName: {}", url, paraFileName); cmd = "python " + pyPre + "mul_queue.py -u " + url + " -f " + paraFileName; Process pr = Runtime.getRuntime().exec(cmd); BufferedReader in = new BufferedReader(new InputStreamReader(pr.getInputStream())); String result = null; String line; while ((line = in.readLine()) != null) { result = line; System.out.println("creating: " + result); log.info("creating: {}", result); } if (result != null && result.contains("completed:")) { sum = Integer.parseInt(result.split(":")[1]); } in.close(); pr.waitFor(); } } catch (Exception e) { e.printStackTrace(); log.info("多线程生成报告出错: {} ", e.getMessage()); } return sum; } / * map写进文件里 * // a = {'a': 'hangge', 'b': 'man', 'school': 'wust'} * @param sampleMap * @param paraFileName * @return */ public static boolean writeMapFile(Map 
      
        sampleMap, String paraFileName) { boolean res = false; BufferedWriter bw = null; try { File file = new File(paraFileName); if (!file.exists()) { CommonUtil.createFile(paraFileName); } bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(paraFileName))); if (sampleMap.size() > 0) { bw.write('{'); int index = 0; for (String key : sampleMap.keySet()) { bw.write('\''); bw.write(key); bw.write('\''); bw.write(':'); bw.write('\''); bw.write(sampleMap.get(key)); bw.write('\''); if (index < sampleMap.size() - 1) { bw.write(','); } index++; } bw.write('}'); res = true; } } catch (Exception e) { e.printStackTrace(); }try { if (bw != null) { bw.close(); } } catch (IOException e) { e.printStackTrace(); } return res; } 
       
      
     
    
  

2、python脚本

import threading import ast from Queue import Queue import pdfkit import sys import getopt import codecs class MyThread(threading.Thread): def __init__(self, q): super(MyThread, self).__init__() self.q = q def run(self): while True: url_path = self.q.get() url_in = url_path[0] path = url_path[1] createpdf(url=url_in, path=path) self.q.task_done() def createpdf(url, path): options = { 'margin-top': '0in', 'margin-right': '0in', 'margin-bottom': '0in', 'margin-left': '0in', 'encoding': "UTF-8", 'javascript-delay': '', } num = 0 compete = pdfkit.from_url(url, path, options=options) if compete: num = 1 return num if __name__ == '__main__': parameterList = sys.argv[1:] url = '' file_name = '' opts, args = getopt.getopt(parameterList, "u:f:", ['url=', 'file_name=']) for opt, arg in opts: if opt in ("-u", "--url"): url = arg elif opt in ("-f", "--file_name"): file_name = arg # print('url:', url) # print('file_name:', file_name) sample_map = {} f = codecs.open(filename=file_name, mode="r+", encoding='utf-8') lines = f.readlines() sample_map_string = '' for line in lines: sample_map_string = line break sample_map = ast.literal_eval(sample_map_string) queue = Queue() size = len(sample_map) stable_num = 5 if size > stable_num: size = stable_num for x in range(size): worker = MyThread(queue) worker.daemon = True worker.start() for i in sample_map.keys(): url_path_list = [url + '?sample_sn=%s' % i, sample_map.get(i)] queue.put(url_path_list) queue.join() print "completed:" + bytes(len(sample_map)) 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/202505.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午11:59
下一篇 2026年3月19日 下午11:59


相关推荐

  • 几百万数据放入内存不会把系统撑爆吗?「建议收藏」

    几百万数据放入内存不会把系统撑爆吗?

    2022年2月13日
    42
  • Activiti7工作流+SpringBoot

    Activiti7工作流+SpringBoot一 Activiti 介绍 Activiti 是基于 Apache 许可的开源 BPM 平台 创始人 TomBaeyens 原是 JBPM 架构师 可以理解为与 JBPM 出自同一祖师爷 它提供了 Eclipse 插件 开发可以通过插件直接绘制业务流程图 基于 Spring ibatis 等框架 并在此之上构建了非常清晰的开发框架 是由 Alfresco 软件发布的业务流程管理 BPM 框架 它是覆盖了业务流程管理 工作流 服务

    2026年3月26日
    2
  • 一文搞定BP神经网络——从原理到应用(原理篇)「建议收藏」

    一文搞定BP神经网络——从原理到应用(原理篇)「建议收藏」神经网络结构以及前向传播过程损失函数和代价函数反向传播1矩阵补充知识11矩阵求梯度12海塞矩阵13总结2矩阵乘积和对应元素相乘3反向传播原理四个基础等式4反向传播总结41单样本输入公式表42多样本输入公式表本文小结Hello,对于神经网络的原理,我入门了好多次,每次都觉得懂了,但是其实内部原理并没有理解透彻。经过不懈努力,终于茅塞顿开,遂总结此文。本

    2022年7月20日
    16
  • MVC三层架构(详解)「建议收藏」

    MVC三层架构(详解)「建议收藏」1:初始MVC(1):三层架构三层架构是指:视图层View、服务层Service,与持久层Dao。它们分别完成不同的功能。View层:用于接收用户提交请求的代码在这里编写。Service层:系统的业务逻辑主要在这里完成。Dao层:直接操作数据库的代码在这里编写。为了更好的降低各层间的耦合度,在三层架构程序设计中,采用面向抽象编程。即上层对下层的调用,是通过接口实现的。而下层对上层的真正服务提供者,是下层接口的实现类。服务标准(接口)是相同的,服务提供者(实现类)可以更换。这就

    2022年6月25日
    28
  • 小波去噪程序c语言,小波去噪c语言程序

    小波去噪程序c语言,小波去噪c语言程序小波去噪c语言程序1、小波阈值去噪理论小波阈值去噪就是对信号进行分解,然后对分解后的系数进行阈值处理,最后重构得到去噪信号。该算法其主要理论依据是:小波变换具有很强的去数据相关性,它能够使信号的能量在小波域集中在一些大的小波系数中;而噪声的能量却分布于整个小波域内。因此,经小波分解后,信号的小波系数幅值要大于噪声的系数幅值。可以认为,幅值比较大的小波系数一般以信号为主,而幅值比较小的系数在很大程度…

    2022年6月17日
    37
  • 关于VUE双向绑定失效的问题「建议收藏」

    关于VUE双向绑定失效的问题「建议收藏」双向绑定失效的原因有很多。lz就说最近遇到的。是的,单价下的那个输入框我用了双向绑定(比如叫price,比如100)。然后ipnut键入中文时,(即使我做了输入验证)。回车时虽然框中不会保留中文,但事实上VUE的双向绑定已经失效了。不管你后面输入什么,绑定的price保存的值只会是中文前的那个值(100)。这样就导致表面好像没事,但是当你提交时就数据不对了。还有

    2025年11月15日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号