java生成PDF的几种方法

作者: 曹元_ | 来源:发表于2020-12-20 07:26 被阅读0次

    问题场景

    总结一下用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、程序生成与下载。

    /**
         * 抠模板
         * @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();
                }
            }
        }
    

    B: 常用功能推荐列表

    ITEXT PDF文件的拆分与合并

    ITEXT 表格的指定列合并-升级版

    ITEXT 表格的指定列合并

    ITEXT 实现背景色交替的三线表

    ITEXT 页眉页脚页码三件套

    ITEXT 目录生成的第三种方法(同时带有书签功能)

    ITEXT-定位PDF中图片的坐标与页码

    ITEXT-PDF彩色字体显示-支持中文

    ITEXT-插入水印(itext-pdfa-5.5.6.jar)

    直接上代码

    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;
        }
    }
    

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

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

    1、java代码

    /**
         * 多线程生成报告
         * @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<String, String> map, Map<String, String> productMap,
                                     List<String> 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<String, String> 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<String, String> 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': '200000',
        }
        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))
    

    相关文章

      网友评论

        本文标题:java生成PDF的几种方法

        本文链接:https://www.haomeiwen.com/subject/fmdlsktx.html