java向PDF文件写入 文字和图片

java中使用itextPdf向PDF文件写入 文字和图片

pdf作为一种阅读体验性非常好的文件格式,那么在实际项目中也经常会涉及到PDF文件的操作,

在公司中,我遇到实现签署文件的这样一个功能,用itextPdf 实现,在网上看了一些文章,总结了一下,写了个工具类

官方地址:https://itextpdf.com,这里使用的是iText-5.0.6版本。

需求:

向pdf文件中的单元格写入文字,和在申请人签名中写入签名图片

在这里插入图片描述
在这里插入图片描述

一、 我们创建好项目后,先导入这次需要的 maven依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.4.3</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

二、了解iText

使用iText 需要知道文字写入的位置怎么确定的,iText是以pdf 文件的左下角为原点的 xy 坐标

在这里插入图片描述

  1. 那么怎样去确定 要写入文字的位置呢?

package com.meditrusthealth.fast.ep.core;

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;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

            System.out.println("text:" + text + "--x:" + rect.x + "--y:" + rect.y + "--width:" + rect.width + "--height:" + rect.height);

            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) {

    }
}

package com.meditrusthealth.fast.ep.core;

import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import org.junit.Test;

import java.util.List;
import java.util.Map;

public class PrintTextLocations {
    @Test
    public void test() throws Exception {
        PdfReader reader = new PdfReader("D:\\work\\11\\支付申请书.pdf");
        //新建一个PDF解析对象
        PdfReaderContentParser parser = new PdfReaderContentParser(reader);
        //包含了PDF页面的信息,作为处理的对象
        //PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("d:/test.pdf"));
        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()) {
                    System.out.println(entry.getKey() + "---" + entry.getValue());
                }
            }
        }
    }
}

在 PrintTextLocations的test()中给出要解析位置的pdf的路径之后,运行程序,在控制台可以看到结果

控制台打印了pdf中文字的坐标
在这里插入图片描述
这个时候,我们选择 箭头的位置, 生成的 文字就会正好是 姓名右边的框框中间的位置,

三、使用java程序向pdf文件写入数据

package com.xsqwe.test;

public class PdfPro {
    private String text;  // 文本
    private float x; //x 坐标
    private float y; //y 坐标

    public PdfPro() {
    }

    public PdfPro(float x, float y, String text) {
        this.text = text;
        this.x = x;
        this.y = y;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }
}

package com.xsqwe.test;

public class ImagePro {
    private float x; //x 坐标
    private float y; //y 坐标
    private float scalePercent;  //缩放百分比
    private String imgPath; //路径

    public ImagePro() {
    }

    public ImagePro(float x, float y, float scalePercent, String imgPath) {
        this.x = x;
        this.y = y;
        this.scalePercent = scalePercent;
        this.imgPath = imgPath;
    }

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }

    public float getScalePercent() {
        return scalePercent;
    }

    public void setScalePercent(float scalePercent) {
        this.scalePercent = scalePercent;
    }

    public String getImgPath() {
        return imgPath;
    }

    public void setImgPath(String imgPath) {
        this.imgPath = imgPath;
    }
}

package com.xsqwe.utils;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.List;

import com.itextpdf.text.Element;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfGState;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.xsqwe.test.ImagePro;
import com.xsqwe.test.PdfPro;

/**
 * @author gaojiabao
 * @date 2020年9月20日 18:14:06
 * @version 1.0.0
 */
public class EditorPDF {

    /**
     * @param inputPDFFilePath  要写入的pdf文件路径
     * @param outPutPDFFilePath 输出的pdf文件路径
     * @param imagePros         要写入的图片的list,包含图片坐标等
     * @param pdfList           要写入的文字的list,包含坐标等
     * @throws Exception
     */
    public static void writeToPdf(String inputPDFFilePath, String outPutPDFFilePath, List<ImagePro> imagePros, List<PdfPro> pdfList)
            throws Exception {
        //append 追加
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(outPutPDFFilePath), false));
        PdfReader reader = new PdfReader(inputPDFFilePath);
        PdfStamper stamper = new PdfStamper(reader, bos);
        int total = reader.getNumberOfPages() + 1;
        PdfContentByte content;
        // BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
        // "c:\\windows\\fonts\\SIMHEI.TTF" 使用windows系统的黑体
        BaseFont base = BaseFont.createFont("c:\\windows\\fonts\\SIMHEI.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        PdfGState gs = new PdfGState();
        for (int i = 1; i < total; i++) {
            //content = stamper.getOverContent(i);// 在内容上方加水印
            content = stamper.getUnderContent(i);//在内容下方加水印
            gs.setFillOpacity(0.2f);
            content.beginText();
            //字体大小
            content.setFontAndSize(base, 10.5F);
            //content.setTextMatrix(390, 810);
            //内容居中,横纵坐标,偏移量
            for (PdfPro pdfPro : pdfList) {
                content.showTextAligned(Element.ALIGN_CENTER, pdfPro.getText(), pdfPro.getX(), pdfPro.getY(), 0);
            }
            for (ImagePro imagePro : imagePros) {
                //添加图片
                Image image = Image.getInstance(imagePro.getImgPath());
            /*
              img.setAlignment(Image.LEFT | Image.TEXTWRAP);
              img.setBorder(Image.BOX); img.setBorderWidth(10);
              img.setBorderColor(BaseColor.WHITE); img.scaleToFit(100072);//大小
              img.setRotationDegrees(-30);//旋转
             */
                //图片的位置(坐标)
                image.setAbsolutePosition(imagePro.getX(), imagePro.getY());
                // image of the absolute
                image.scaleToFit(200, 200);
                image.scalePercent(imagePro.getScalePercent());//依照比例缩放. 调整缩放,控制图片大小
                content.addImage(image);
            }
            content.setFontAndSize(base, 8);
            content.endText();
        }
        stamper.close();
        //关闭打开的原来PDF文件,不执行reader.close()删除不了(必须先执行stamper.close(),否则会报错)
        reader.close();
        //删除原来的PDF文件
        /*File targetTemplePDF = new File(inputPDFFilePath);
        targetTemplePDF.delete();*/
    }
}

执行完 main 方法后, 可以看到效果

 public static void main(String[] args) throws Exception {
        List<PdfPro> pdfList = new ArrayList<>();
        //患者姓名
        //text: --x:179.54--y:604.86--width:5.279999--height:11.071045
        pdfList.add(new PdfPro(179.54f, 718.62f, "张三"));
        //性别
        //text: --x:328.27--y:539.43--width:5.279999--height:11.071045
        pdfList.add(new PdfPro(328.27f, 718.62f, "男"));
        //患者电话
        pdfList.add(new PdfPro(250.25f, 702.42f, "13637222876"));
        //身份证号
        pdfList.add(new PdfPro(476.86f, 702.42f, "410122190910130294"));
        //户名
        pdfList.add(new PdfPro(476.74f, 654.18f, "6217477777888888214"));
        ArrayList<ImagePro> imgList = new ArrayList();
        // scalePercent 缩放比例
        imgList.add(new ImagePro(130, 48, 15, "D:\\work\\11\\新建位图图像.bmp"));
        writeToPdf("D:\\work\\11\\支付申请书.pdf", "D:\\work\\11\\测试PDF01.pdf", imgList, pdfList);
    }

这里传入的文字坐标,一般为解析坐标之后,打印的后两个

text: --x:179.54--y:604.86--width:5.279999--height:11.071045

写入文字和图片后

在这里插入图片描述
在这里插入图片描述
可以看到文字,正好居中在后面的单元格,图片也正好在横线上
完成


版权声明:本文为weixin_44197039原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。