JAVA用word文档生成或转换成PDF文档,并支持水印、页眉、页脚功能

时间:2024-03-19 07:26:40

之前发布的JAVA生成PDF相关功能的博客不够完善,最近又结合了添加水印与页眉页脚的功能,故在此重新梳理一版,结合实际代码,希望能帮到大家

功能准备:

本功能基于freemaker+openoffice+itext实现通过DOCX模板生成PDF文件或者直接转换DOC到PDF,支持水印页眉、页脚。

相关依赖,需要事先安装好openoffice并开启服务(自行百度)。

    <dependency>
            <groupId>com.artofsolving</groupId>
            <artifactId>jodconverter</artifactId>
            <version>2.2.1</version>
        </dependency>
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>itextpdf</artifactId>
        <version>5.5.10</version>
    </dependency> 
    <!-- https://mvnrepository.com/artifact/com.itextpdf/itext-asian -->
    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>itext-asian</artifactId>
        <version>5.2.0</version>
    </dependency>

1. 生成docx模板和xml模板

生成docx与XML模板,这样做是使结合数据生成的DOC文件是真正的.DOC格式的文件,而不是只是将后缀名改为.doc的XML文件(因为这种文件有些软件与浏览器解析不了,显示的是XML文件的内容)

准备需要用的DOCX的文档如test.docx作为DOCX模板。

用压缩文件打开test.docx,复制word目录下的document.xml,取出来作为XML模板。

 

JAVA用word文档生成或转换成PDF文档,并支持水印、页眉、页脚功能

 

 

JAVA用word文档生成或转换成PDF文档,并支持水印、页眉、页脚功能

XML模板里用${参数}代表传入的参数,如需遍历的话用<#list datalist(传入的集合名称) as datalist >  ${datalist.shuxing} </#list>来控制遍历的开关 

2.用freemaker填充模板数据,然后执行相关转换方法,最终下载。

 /**
     * @方法名称 
     * @功能描述 生成下载PDF
     * @作者 fan
     * @创建时间 
     * @param dataMap 模板参数
     * @param xmlTempName xml模板名称 (即前文准备的test.xml)
     * @param docxTempName docx模板名称(即前文准备的test.docx)
     * @param pdfName 生成的PDF名称
     * @param response
     * @param request
     * @return
     */
    public static void downloadPdf(Map<String, Object> dataMap, String xmlTempName, String docxTempName, String pdfName,
            HttpServletResponse response, HttpServletRequest request) {
        try {
            // Configuration 用于读取ftl文件
            Configuration configuration = new Configuration(new Version("2.3.0"));
            configuration.setDefaultEncoding("utf-8");
            // 指定路径的第一种方式(根据某个类的相对路径指定)
            // configuration.setClassForTemplateLoading(this.getClass(), "");
            // 指定路径的第二种方式,我的路径是C:/a.ftl
            // configuration.setDirectoryForTemplateLoading(new File("c:/"));
            configuration.setServletContextForTemplateLoading(request.getSession().getServletContext(), "./WEB-INF/template");
            // 输出文档路径及名称
            File outFile = new File(request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp.xml"));
            // 以utf-8的编码读取ftl文件
            Template template = configuration.getTemplate(xmlTempName, "utf-8");
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "utf-8"), 10240);
            template.process(dataMap, out);//将初始模板结合数据得到新的xml模板
            out.close();
            String wordFile = request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp.doc");    
            String pdfFile = request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp.pdf");
            outDocx(new File(request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp.xml")), docxTempName, wordFile, request);//将结合数据后的XML模板转换为标准的DOC格式文档
            WordToPDF(wordFile, pdfFile);//将标准的DOC格式文档转换为PDF格式
            addFooterAndWater(pdfFile, request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp22.pdf"), "水印", "页眉", "页脚");//为PDF添加水印、页眉页脚等
            download(request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp22.pdf"), pdfName, response, false);
            new File(wordFile).delete();//下载完成后删除生成的模板文件
            new File(pdfFile).delete();
            new File(request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp22.pdf")).delete();
            outFile.delete();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3.   生成标准的DOC文件方法

 /**
     * @方法名称 
     * @功能描述 生成下载PDF
     * @作者 fan
     * @创建时间 
     * @param documentFile 原始xml结合数据集生成的xml模板
     * @param docxTemplate 初始准备的docx格式的模板路径
     * @param toFilePath 生成的文档路径(后缀名取.doc)
     * @param response
     * @param request
     * @return
     */

 // 生成标准的DOC文件方法
    public static void outDocx(File documentFile, String docxTemplate, String toFilePath, HttpServletRequest request)
            throws ZipException, IOException {

        try {
            File docxFile = new File(request.getSession().getServletContext().getRealPath("/WEB-INF/template/" + docxTemplate));
            ZipFile zipFile = new ZipFile(docxFile);
            Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries();
            ZipOutputStream zipout = new ZipOutputStream(new FileOutputStream(toFilePath));
            int len = -1;
            byte[] buffer = new byte[1024];
            while (zipEntrys.hasMoreElements()) {
                ZipEntry next = zipEntrys.nextElement();
                InputStream is = zipFile.getInputStream(next);
                // 把输入流的文件传到输出流中 如果是word/document.xml由我们输入
                zipout.putNextEntry(new ZipEntry(next.toString()));
                if ("word/document.xml".equals(next.toString())) {
                    // InputStream in = new FileInputStream(new
                    // File(XmlToDocx.class.getClassLoader().getResource("").toURI().getPath()+"template/test.xml"));
                    InputStream in = new FileInputStream(documentFile);
                    while ((len = in.read(buffer)) != -1) {
                        zipout.write(buffer, 0, len);
                    }
                    in.close();
                } else {
                    while ((len = is.read(buffer)) != -1) {
                        zipout.write(buffer, 0, len);
                    }
                    is.close();
                }
            }
            zipout.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

4.   WORD转换为PDF

  // word转化为PDF方法
    public static void WordToPDF(String startFile, String overFile) throws IOException {
        // 源文件目录
        File inputFile = new File(startFile);
        if (!inputFile.exists()) {
            System.out.println("源文件不存在!");
            return;
        }

        // 输出文件目录
        File outputFile = new File(overFile);
        if (!outputFile.getParentFile().exists()) {
            outputFile.getParentFile().exists();
        }

        // 调用openoffice服务线程
        /** 我把openOffice下载到了 C:/Program Files (x86)/下  ,下面的写法自己修改编辑就可以**/


        // 连接openoffice服务
        OpenOfficeConnection connection = new SocketOpenOfficeConnection("127.0.0.1", 8100);
        connection.connect();

        // 转换
        DocumentConverter converter = new OpenOfficeDocumentConverter(connection);
        converter.convert(inputFile, outputFile);

        // 关闭连接
        connection.disconnect();

        // 关闭进程
//        p.destroy();

    }

4.添加水印、页眉、页脚等

/**
	 *	添加水印、页眉、页脚
	 * @param fileName 源文件路径
	 * @param savepath 目标文件路径
	 * @param waterMarkName 文字水印
	 * @param pageHeade 页眉
	 * @param foot 页脚
	 * @return
	 */

  public static int addFooterAndWater(String fileName, String savepath,
            String waterMarkName, String pageHeade, String foot)
    {
        // 文档总页数
        int num = 0;

        Document document = new Document();
        try
        {
            PdfReader reader = new PdfReader(fileName);
            BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",
                    BaseFont.EMBEDDED);

            num = reader.getNumberOfPages();
            PdfCopy copy = new PdfCopy(document, new FileOutputStream(savepath));
            document.open();
            for (int i = 0; i < num;)
            {
                PdfImportedPage page = copy.getImportedPage(reader, ++i);
                PageStamp stamp = copy.createPageStamp(page);
                Font f = new Font(base);

                // 添加页脚,左侧文字,右侧页码
                ColumnText.showTextAligned(stamp.getUnderContent(),
                        Element.ALIGN_RIGHT,
                        new Phrase(String.format("第 %d 页/共 %d 页", i, num), f),
                        550f, 28, 0);
                ColumnText.showTextAligned(stamp.getUnderContent(),
                        Element.ALIGN_LEFT, new Phrase(foot, f), 50f, 28, 0);

                // 添加页眉 (文字页眉,居中)
                ColumnText.showTextAligned(stamp.getUnderContent(),
                        Element.ALIGN_CENTER, new Phrase(pageHeade, f), 150f,
                        800, 0);
                
                // 页眉添加logo (图片页眉,居右)
                /*Image img = Image.getInstance("template/logo.png");// 选择图片
                img.setAlignment(1);
                img.scaleAbsolute(436 / 5, 96 / 5);// 控制图片大小
                img.setAbsolutePosition(450f, 800);// 控制图片位置
                stamp.getUnderContent().addImage(img);*/

                // 添加水印
                PdfContentByte under = stamp.getUnderContent();
                under.beginText();
                under.setColorFill(BaseColor.BLACK);
                
                // 字符越长,字体越小,设置字体
                int fontSize = getFontSize(waterMarkName);
                under.setFontAndSize(base, fontSize);

                // 设置水印文字字体倾斜 开始
                float pageWidth = reader.getPageSize(i).getWidth();
                float pageHeight = reader.getPageSize(i).getHeight();

                under.showTextAligned(Element.ALIGN_CENTER, waterMarkName,
                        pageWidth / 2, pageHeight / 2, 60);// 水印文字成60度角倾斜,且页面居中展示

                // 字体设置结束
                under.endText();
                stamp.alterContents();
                copy.addPage(page);
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return -1;
        }
        finally
        {
            if (null != document)
            {
                document.close();
            }
        }
        System.out.println("pdf totalpages:" + num);
        return num;

    }
    /**
     * 根据水印文字长度计算获取字体大小
     * @param waterMarkName
     * @return
     */
    private static int getFontSize(String waterMarkName){
        int fontSize = 80;
        if(null != waterMarkName && !"".equals(waterMarkName)){
            int length = waterMarkName.length();
            if(length <=26 && length >= 18){
                fontSize = 26;
            }else if(length <18 && length >= 8){
                fontSize = 40;
            }else if(length <8 && length >= 1){
                fontSize = 80;
            }else {
                fontSize = 16;
            }
        }       
        return fontSize;
    }

 

 

//经过本人测试,2.2.1的jodconverter包只能转换后缀doc格式的文件,不能转换docx的文件,有人说应是jodconverter版本不够,但博主当时下载最高版本后,其余方法又会报异常。有问题欢迎讨论,水印功能参考https://www.it610.com/article/3640077.htm