利用.ftl模板生成word之后再转pdf发现 填充的印章图片显示不全,没有自动移到下一页处理办法

因为公司业务需要,利用一个ftl模板生成word在转为pdf。然后发现印章图片在页底那边显示不全,没有如文字一般自动换到下一页去。例如如下:

最后折腾了半天,在网上试了各种办法。最后只能通过 java提取PDF文字坐标。来算出印章图片坐标,根据坐标y轴测出印章图片极限安全位置y坐标,如果大于该坐标y则表示该印章图片显示在同一页且图片显示完全,如果小于该坐标则表示图片显示不全,这时候只能换一种逻辑,写两个ftl模板,一个只显示印章图片前的数据,一个只显示印章图片之后的(包括印章),最后将两个pdf合并为一个。这种办法不太好,太麻烦了。。但是业务紧急,暂时也找不到更好的办法去处理。凑合凑合吧。

java提取PDF文字坐标 可以参考https://blog.csdn.net/sinat_29957455/article/details/78759654

我对他的方法稍微做了点改进以更适应我的需求。

1.如何生成ftl模板

之后在重命名xml为ftl文件就行。

2.pdf提取关键字Util工具类

①通过定义一个类实现RenderListener,可以通过里面的几个方法来操作PDF中的文字和图片

package cn.xxx.common.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.awt.geom.RectangularShape;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
public class TestRenderListener implements RenderListener {
    //用来存放文字的矩形
    List<Rectangle2D.Float> rectText = new ArrayList<Rectangle2D.Float>();
    //用来存放文字
    List<String> textList = new ArrayList<String>();
    //用来存放文字的y坐标
    List<Float> listY = new ArrayList<Float>();
    //用来存放每一行文字的坐标位置
    List<Map<String,Rectangle2D.Float>> rows_text_rect = new ArrayList<>();
    //PDF文件的路径
    protected String filepath = null;
    public TestRenderListener() {
    }

    //step 2,遇到"BT"执行
    @Override
    public void beginTextBlock() {
        // TODO Auto-generated method stub
    }

    //step 3
    /**
     * 文字主要处理方法
     */
    @Override
    public void renderText(TextRenderInfo renderInfo) {
        //获取文字的下面的矩形
        //Rectangle2D.Float rectBase = renderInfo.getBaseline().getBoundingRectange();


        String text = renderInfo.getText();
        if(text.length() > 0){
            RectangularShape rectBase = renderInfo.getBaseline().getBoundingRectange();
            //获取文字下面的矩形
            Rectangle2D.Float rectAscen = renderInfo.getAscentLine().getBoundingRectange();
            //计算出文字的边框矩形
            float leftX = (float) rectBase.getMinX();
            float leftY = (float) rectBase.getMinY()-1;
            float rightX = (float) rectAscen.getMaxX();
            float rightY = (float) rectAscen.getMaxY()+1;

            Rectangle2D.Float rect = new Rectangle2D.Float(leftX, leftY, rightX - leftX, rightY - leftY);


            if(listY.contains(rect.y)){
                int index = listY.indexOf(rect.y);
                float tempx = rect.x > rectText.get(index).x ? rectText.get(index).x : rect.x;
                rectText.set(index,new Rectangle2D.Float(tempx,rect.y,rect.width + rectText.get(index).width,rect.height));
                textList.set(index,textList.get(index) + text);
            }else{
                rectText.add(rect);
                textList.add(text);
                listY.add(rect.y);
            }

            Map<String,Rectangle2D.Float> map = new HashMap<>();
            map.put(text,rect);
            rows_text_rect.add(map);
        }
    }
    //step 4(最后执行的,只执行一次),遇到“ET”执行
    @Override
    public void endTextBlock() {
        // TODO Auto-generated method stub
    }

    //step 1(图片处理方法)
    @Override
    public void renderImage(ImageRenderInfo renderInfo) {

    }
}

②pdf提取关键字Util工具类 (下载一个itextpdf jar包 我的是5.5.13版本)    不要急着复制,因为这个工具类是一段一段提取pdf文字坐标  所以为了知道自己的关键词在不在他的词分析器里需要进行测试。测试完知道自己的关键词后 放开当中部分一段注释的逻辑代码并修改,则就可以返回唯一的关键词坐标y。

package cn.xxx.common.util;

import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;

public class WordInPdfLocatonUtil {
    /**
     *
     * @param keyWord  关键词
     * @param pdfPath pdf 文件路径
     * @return 关键词所在该页pdf坐标y轴 
     */
    public static Float getY(String keyWord,String pdfPath) {
        Float locationY = null; //关键词坐标y轴
        PdfReader reader = null;
        FileOutputStream out =null;
        try {
            reader = new PdfReader(pdfPath);
            //新建一个PDF解析对象
            PdfReaderContentParser parser = new PdfReaderContentParser(reader);
            //包含了PDF页面的信息,作为处理的对象
            out =  new FileOutputStream("d:test.pdf");
            PdfStamper stamper = new PdfStamper(reader, out);
            for (int i = 1; i <= reader.getNumberOfPages(); i++) {
                //新建一个ImageRenderListener对象,该对象实现了RenderListener接口,作为处理PDF的主要类
                TestRenderListener listener = new TestRenderListener();
                //解析PDF,并处理里面的文字
                parser.processContent(i, listener);
                //获取文字的矩形边框
                List<Rectangle2D.Float> rectText = listener.rectText;
                List<String> textList = listener.textList;
                List<Float> listY = listener.listY;
                List<Map<String, Rectangle2D.Float>> list_text = listener.rows_text_rect;
                for (int k = 0; k < list_text.size(); k++) {
                    Map<String, Rectangle2D.Float> map = list_text.get(k);
                    for (Map.Entry<String, Rectangle2D.Float> entry : map.entrySet()) {
                        //判断与关键词是否相等,为了防止出现关键词没找到,缩小关键词范围匹配
//                        if(entry.getKey().indexOf(keyWord)==-1){
//                            if(keyWord.length()>=3){
//                                String firstKeyWord=keyWord;
//                                keyWord=keyWord.substring(0,2);// “此函”
//                                if(entry.getKey().indexOf(keyWord)==-1){
//                                    keyWord = firstKeyWord.substring(1,3);//“函告”
//                                }
//                            }
//                        }
//                        if (entry.getKey().indexOf(keyWord) > -1) {//只打印并返回关键词坐标
                            float y = entry.getValue().y;
                            locationY = y ;
                            System.out.println(entry.getKey() + "---" + y);
//                        }
                    }
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("文件未找到");
        } catch (DocumentException e) {

            e.printStackTrace();
        }finally {
            try {
                reader.close();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return locationY;
    }

    public static void main(String[] args) {
        getY("此函告","D:\\hzresources\\test.pdf");
    }
}

 

如果注释不放开打印的是如下所有字段与y轴坐标。

 

如果注释放开就只返回关键词对应坐标y,打印的就是第二幅图。

3.合并pdf工具类 (itext-2.1.7.jar)

package cn.xxx.common.util;

import java.io.FileOutputStream;
import java.io.IOException;
import com.lowagie.text.Document;
import com.lowagie.text.pdf.PdfCopy;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;

public class PdfMergeUtil {
    public static void main(String[] args) {


        String[] files = {"D:\\hzresources\\4928F159-4A62-B018-402A-AED4C578AD17.pdf",
                "D:\\hzresources\\4928F159-4A62-B018-402A-AED4C578AD17pic.pdf"};
        mergePdfFiles(files, "D:\\hzresources\\merge.pdf");
    } 


    /**
     * 
     * @param files  合並pdf文件数组
     * @param newfile  合并后pdf路径
     * @return 成功返回true, 否則返回false
     */

    public static boolean mergePdfFiles(String[] files, String newfile) {
        boolean retValue = false;
        Document document = null;
        FileOutputStream out =null;
        try {
            document = new Document(new PdfReader(files[0]).getPageSize(1));
            out = new FileOutputStream(newfile);
            PdfCopy copy = new PdfCopy(document, out);
            document.open();
            for (int i = 0; i < files.length; i++) {
                PdfReader reader = new PdfReader(files[i]);
                int n = reader.getNumberOfPages();
                for (int j = 1; j <= n; j++) {
                    document.newPage();
                    PdfImportedPage page = copy.getImportedPage(reader, j);
                    copy.addPage(page);
                }
            }
            retValue = true;

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            document.close();
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return retValue;
    }
}

最后只要合并pdf就可以了,这样就完美的解决了图片在页底显示不全的问题。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
FreeMarker 是一个Java模板引擎,它可以帮助我们根据模板生成各种文件,包括 Word 文档。下面是一个简单的示例,演示如何使用 FreeMarker 创建一个根据 FTL 模板生成 Word 文档的过程: 1. 创建一个 FreeMarker 的配置对象,并设置模板文件所在的目录: ``` Configuration cfg = new Configuration(Configuration.VERSION_2_3_30); cfg.setDirectoryForTemplateLoading(new File("path/to/ftl/templates")); ``` 2. 从配置对象中获取模板对象: ``` Template template = cfg.getTemplate("template.ftl"); ``` 3. 准备数据模型,可以是一个 JavaBean、Map 或者其他类型的对象: ``` Map<String, Object> data = new HashMap<>(); data.put("title", "Hello, World!"); data.put("content", "This is a test document created by FreeMarker."); ``` 4. 创建一个 Writer 对象,用于输出生成Word 文档: ``` Writer out = new FileWriter(new File("path/to/output/doc.docx")); ``` 5. 将数据模型和 Writer 对象传递给模板对象,生成 Word 文档: ``` template.process(data, out); ``` 完整的示例代码如下: ``` import freemarker.template.Configuration; import freemarker.template.Template; import java.io.File; import java.io.FileWriter; import java.io.Writer; import java.util.HashMap; import java.util.Map; public class FreeMarkerDemo { public static void main(String[] args) throws Exception { // 创建 Configuration 对象 Configuration cfg = new Configuration(Configuration.VERSION_2_3_30); cfg.setDirectoryForTemplateLoading(new File("path/to/ftl/templates")); // 获取模板对象 Template template = cfg.getTemplate("template.ftl"); // 准备数据模型 Map<String, Object> data = new HashMap<>(); data.put("title", "Hello, World!"); data.put("content", "This is a test document created by FreeMarker."); // 创建输出流 Writer out = new FileWriter(new File("path/to/output/doc.docx")); // 生成 Word 文档 template.process(data, out); // 关闭输出流 out.close(); } } ``` 注意,上面的示例代码中使用的模板文件是 FTL 格式,如果要生成 Word 文档,还需要将模板文件换成 docx 或者其他 Word 文档格式。常见的工具包括 Apache POI 和 Docx4j 等。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值