java生成PDF的几种方法

x33g5p2x  于2021-12-28 转载在 其他  
字(9.4k)|赞(0)|评价(0)|浏览(432)

问题场景

总结一下用java生成PDF的方法:
A、itext-PdfStamper pdfStamper(俗称抠模板)
B、itext-Document document(正常代码撰写)
C、wkhtmltopdf(使用工具)

分析比较

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

用到的资源

A/B: itext-pdfa-5.5.6.jar、itext-xtra-5.5.6.jar、itext-5.5.6.jar、itext-asian.jar
C: wkhtmltopdf-0.12.4、python2.7.14、pdfkit

举例:

A:
1、用PDF编辑器编辑好模板,留出空白等待程序填充。
2、程序生成与下载。

  1. /**
  2. * 抠模板
  3. * @throws Exception
  4. */
  5. public void createAllPdf() throws Exception {
  6. //填充创建pdf
  7. PdfReader reader = null;
  8. PdfStamper stamp = null;
  9. try {
  10. reader = new PdfReader("E:/module.pdf");
  11. SimpleDateFormat simp = new SimpleDateFormat("yyyy-MM-dd");
  12. String times = simp.format(new Date()).trim();
  13. //创建生成报告名称
  14. String root = ServletActionContext.getRequest().getRealPath("/upload") + File.separator;
  15. if (!new File(root).exists())
  16. new File(root).mkdirs();
  17. File deskFile = new File(root, times + ".pdf");
  18. stamp = new PdfStamper(reader, new FileOutputStream(deskFile));
  19. //取出报表模板中的所有字段
  20. AcroFields form = stamp.getAcroFields();
  21. // 填充数据
  22. form.setField("name", "zhangsan");
  23. form.setField("sex", "男");
  24. form.setField("age", "15");
  25. //报告生成日期
  26. SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");
  27. String generationdate = dateformat.format(new Date());
  28. form.setField("generationdate", generationdate);
  29. stamp.setFormFlattening(true);
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. } finally {
  33. if (stamp != null) {
  34. stamp.close();
  35. }
  36. if (reader != null) {
  37. reader.close();
  38. }
  39. }
  40. }

B:
常用功能推荐列表
ITEXT PDF文件的拆分与合并
ITEXT 表格的指定列合并-升级版
ITEXT 表格的指定列合并
ITEXT 实现背景色交替的三线表
ITEXT 页眉页脚页码三件套
ITEXT 目录生成的第三种方法(同时带有书签功能)
ITEXT-定位PDF中图片的坐标与页码
ITEXT-PDF彩色字体显示-支持中文
ITEXT-插入水印(itext-pdfa-5.5.6.jar)
直接上代码

  1. package itext;
  2. import com.itextpdf.text.*;
  3. import com.itextpdf.text.pdf.*;
  4. import java.io.FileNotFoundException;
  5. import java.io.FileOutputStream;
  6. /**
  7. * Created on 2017/5/16
  8. * Author: youxingyang.
  9. */
  10. public class TableAndTitle {
  11. /**
  12. * @param args
  13. */
  14. public static void main(String[] args) throws Exception {
  15. String fileName = "tableAndTitle.pdf";
  16. TableAndTitle.test(fileName);
  17. }
  18. private static void test(String fileName) {
  19. Document document = new Document();
  20. try {
  21. PdfWriter.getInstance(document, new FileOutputStream(fileName));
  22. document.open();
  23. PdfPTable table = new PdfPTable(1);
  24. table.setKeepTogether(true);
  25. table.setSplitLate(false);
  26. PdfPTable table1 = new PdfPTable(1);
  27. PdfPCell cell0 = new PdfPCell();
  28. Paragraph p = new Paragraph("table title sample");
  29. p.setAlignment(1);
  30. p.setSpacingBefore(15f);
  31. cell0.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);
  32. cell0.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);//然并卵
  33. cell0.setPaddingTop(-2f);//把字垂直居中
  34. cell0.setPaddingBottom(8f);//把字垂直居中
  35. cell0.addElement(p);
  36. cell0.setBorder(0);
  37. table1.addCell(cell0);
  38. PdfPTable pTable = new PdfPTable(table1);
  39. document.add(pTable);
  40. PdfPTable table2 = new PdfPTable(2);
  41. float border = 1.5f;
  42. for (int a = 0; a < 20; a++) {
  43. PdfPCell cell = new PdfPCell();
  44. Paragraph pp;
  45. if (a == 0 || a == 1) {
  46. pp = str2ParaByTwoFont("tableTitle" + (a + 1), 9f, BaseColor.BLACK, Font.BOLD); //小五 加粗
  47. cell.setBorderWidthBottom(border);
  48. cell.setBorderWidthTop(border);
  49. } else {
  50. if (a == 18 || a == 19) {
  51. cell.setBorderWidthTop(0);
  52. cell.setBorderWidthBottom(border);
  53. } else {
  54. cell.setBorderWidthBottom(0);
  55. cell.setBorderWidthTop(0);
  56. }
  57. pp = str2ParaByTwoFont("tableContent" + (a - 1), 9f, BaseColor.BLACK); //小五
  58. }
  59. //设置间隔的背景色
  60. if ((a + 1) % 2 == 0) {
  61. if (((a + 1) / 2) % 2 == 1) {
  62. cell.setBackgroundColor(new BaseColor(128, 128, 255));
  63. } else {
  64. cell.setBackgroundColor(new BaseColor(128, 255, 255));
  65. }
  66. } else {
  67. if (((a + 1) / 2) % 2 == 1) {
  68. cell.setBackgroundColor(new BaseColor(128, 255, 255));
  69. } else {
  70. cell.setBackgroundColor(new BaseColor(128, 128, 255));
  71. }
  72. }
  73. pp.setAlignment(1);
  74. cell.setBorderWidthLeft(0);
  75. cell.setBorderWidthRight(0);
  76. cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);
  77. cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);//然并卵
  78. cell.setPaddingTop(-2f);//把字垂直居中
  79. cell.setPaddingBottom(8f);//把字垂直居中
  80. cell.addElement(pp);
  81. table2.addCell(cell);
  82. }
  83. PdfPCell c1 = new PdfPCell();
  84. c1.setBorder(0);
  85. c1.addElement(table1);
  86. PdfPCell c2 = new PdfPCell();
  87. c2.setBorder(0);
  88. c2.addElement(table2);
  89. table.addCell(c1);
  90. table.addCell(c2);
  91. document.add(table);
  92. document.close();
  93. } catch (DocumentException | FileNotFoundException e) {
  94. e.printStackTrace();
  95. }
  96. }
  97. /**
  98. * 两种字体显示文字
  99. *
  100. * @param cont
  101. * @param size
  102. * @param color
  103. * @return
  104. */
  105. private static Paragraph str2ParaByTwoFont(String cont, float size, BaseColor color) {
  106. Paragraph res = new Paragraph();
  107. FontSelector selector = new FontSelector();
  108. //非汉字字体颜色
  109. Font f1 = FontFactory.getFont(FontFactory.TIMES_ROMAN, size);
  110. f1.setColor(color);
  111. //汉字字体颜色
  112. Font f2 = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED, size);
  113. f2.setColor(color);
  114. selector.addFont(f1);
  115. selector.addFont(f2);
  116. Phrase ph = selector.process(cont);
  117. res.add(ph);
  118. return res;
  119. }
  120. /**
  121. * 两种字体显示文字
  122. *
  123. * @param cont
  124. * @param size
  125. * @param color
  126. * @param bold
  127. * @return
  128. */
  129. private static Paragraph str2ParaByTwoFont(String cont, float size, BaseColor color, int bold) {
  130. Paragraph res = new Paragraph();
  131. FontSelector selector = new FontSelector();
  132. //非汉字字体颜色
  133. Font f1 = FontFactory.getFont(FontFactory.TIMES_ROMAN, size);
  134. f1.setColor(color);
  135. f1.setStyle(bold);
  136. //汉字字体颜色
  137. Font f2 = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED, size);
  138. f2.setColor(color);
  139. f2.setStyle(bold);
  140. selector.addFont(f1);
  141. selector.addFont(f2);
  142. Phrase ph = selector.process(cont);
  143. res.add(ph);
  144. return res;
  145. }
  146. }

C:
总思路:java调用python脚本,python下多线程调用了pdfkit(pdfkit 是 wkhtmltopdf 的Python封装包)

CODE: 里面有的简单方法未提供源码,自己写
1、java代码

  1. /**
  2. * 多线程生成报告
  3. * @param map 样本及公司对应集合
  4. * @param productMap 样本及产品对应集合
  5. * @param sampleList 样本列表集合
  6. * @param storeDir 报告存储路径
  7. * @param url 源转换的页面url前缀
  8. * @param pyPre python脚本存放的位置
  9. * @param uuid 本地任务唯一标识
  10. * @param storePrefix url参数文件前缀
  11. * @return
  12. */
  13. private static int createMul(Map<String, String> map, Map<String, String> productMap,
  14. List<String> sampleList, String storeDir, String url, String pyPre, String uuid, String storePrefix) {
  15. String date = DateUtil.date2Str(new Date());
  16. StringBuilder pathTemp = new StringBuilder("");
  17. String companyId;
  18. String productCode;
  19. String cmd;
  20. int sum = 0;
  21. Map<String, String> sampleMap = new LinkedHashMap<>(sampleList.size());
  22. String paraFileName;
  23. try {
  24. String path;
  25. for (String sampleCode : sampleList) {
  26. companyId = map.get(sampleCode);
  27. productCode = productMap.get(sampleCode);
  28. pathTemp.append(storeDir).append(date).append("-").append(uuid).append(File.separator).append(companyId).append(File.separator).append(productCode);
  29. path = pathTemp.toString();
  30. pathTemp.setLength(0);
  31. File file = new File(path);
  32. if (!file.exists()) {
  33. file.mkdirs();
  34. }
  35. path += File.separator + sampleCode + "-" + productCode + ".pdf";
  36. path = path.replace("\\", "/");
  37. sampleMap.put(sampleCode, path);
  38. }
  39. paraFileName = storePrefix + DateUtil.date2Str(new Date()) + "-" + EncryUtil.getUUID() + ".txt";
  40. boolean success = writeMapFile(sampleMap, paraFileName);
  41. if (success) {
  42. log.info("多线程生成报告参数: url: {}, paraFileName: {}", url, paraFileName);
  43. cmd = "python " + pyPre + "mul_queue.py -u " + url + " -f " + paraFileName;
  44. Process pr = Runtime.getRuntime().exec(cmd);
  45. BufferedReader in = new BufferedReader(new InputStreamReader(pr.getInputStream()));
  46. String result = null;
  47. String line;
  48. while ((line = in.readLine()) != null) {
  49. result = line;
  50. System.out.println("creating: " + result);
  51. log.info("creating: {}", result);
  52. }
  53. if (result != null && result.contains("completed:")) {
  54. sum = Integer.parseInt(result.split(":")[1]);
  55. }
  56. in.close();
  57. pr.waitFor();
  58. }
  59. } catch (Exception e) {
  60. e.printStackTrace();
  61. log.info("多线程生成报告出错: {} ", e.getMessage());
  62. }
  63. return sum;
  64. }
  65. /**
  66. * map写进文件里
  67. * // a = {'a': 'hangge', 'b': 'man', 'school': 'wust'}
  68. * @param sampleMap
  69. * @param paraFileName
  70. * @return
  71. */
  72. public static boolean writeMapFile(Map<String, String> sampleMap, String paraFileName) {
  73. boolean res = false;
  74. BufferedWriter bw = null;
  75. try {
  76. File file = new File(paraFileName);
  77. if (!file.exists()) {
  78. CommonUtil.createFile(paraFileName);
  79. }
  80. bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(paraFileName)));
  81. if (sampleMap.size() > 0) {
  82. bw.write('{');
  83. int index = 0;
  84. for (String key : sampleMap.keySet()) {
  85. bw.write('\'');
  86. bw.write(key);
  87. bw.write('\'');
  88. bw.write(':');
  89. bw.write('\'');
  90. bw.write(sampleMap.get(key));
  91. bw.write('\'');
  92. if (index < sampleMap.size() - 1) {
  93. bw.write(',');
  94. }
  95. index++;
  96. }
  97. bw.write('}');
  98. res = true;
  99. }
  100. } catch (Exception e) {
  101. e.printStackTrace();
  102. }try {
  103. if (bw != null) {
  104. bw.close();
  105. }
  106. } catch (IOException e) {
  107. e.printStackTrace();
  108. }
  109. return res;
  110. }

2、python脚本

  1. import threading
  2. import ast
  3. from Queue import Queue
  4. import pdfkit
  5. import sys
  6. import getopt
  7. import codecs
  8. class MyThread(threading.Thread):
  9. def __init__(self, q):
  10. super(MyThread, self).__init__()
  11. self.q = q
  12. def run(self):
  13. while True:
  14. url_path = self.q.get()
  15. url_in = url_path[0]
  16. path = url_path[1]
  17. createpdf(url=url_in, path=path)
  18. self.q.task_done()
  19. def createpdf(url, path):
  20. options = {
  21. 'margin-top': '0in',
  22. 'margin-right': '0in',
  23. 'margin-bottom': '0in',
  24. 'margin-left': '0in',
  25. 'encoding': "UTF-8",
  26. 'javascript-delay': '200000',
  27. }
  28. num = 0
  29. compete = pdfkit.from_url(url, path, options=options)
  30. if compete:
  31. num = 1
  32. return num
  33. if __name__ == '__main__':
  34. parameterList = sys.argv[1:]
  35. url = ''
  36. file_name = ''
  37. opts, args = getopt.getopt(parameterList, "u:f:", ['url=', 'file_name='])
  38. for opt, arg in opts:
  39. if opt in ("-u", "--url"):
  40. url = arg
  41. elif opt in ("-f", "--file_name"):
  42. file_name = arg
  43. # print('url:', url)
  44. # print('file_name:', file_name)
  45. sample_map = {}
  46. f = codecs.open(filename=file_name, mode="r+", encoding='utf-8')
  47. lines = f.readlines()
  48. sample_map_string = ''
  49. for line in lines:
  50. sample_map_string = line
  51. break
  52. sample_map = ast.literal_eval(sample_map_string)
  53. queue = Queue()
  54. size = len(sample_map)
  55. stable_num = 5
  56. if size > stable_num:
  57. size = stable_num
  58. for x in range(size):
  59. worker = MyThread(queue)
  60. worker.daemon = True
  61. worker.start()
  62. for i in sample_map.keys():
  63. url_path_list = [url + '?sample_sn=%s' % i, sample_map.get(i)]
  64. queue.put(url_path_list)
  65. queue.join()
  66. print "completed:" + bytes(len(sample_map))

相关文章