欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java使用itextpdf生成pdf并填充自定义数据

程序员文章站 2022-06-01 08:27:32
...

项目中有个需求,对于已有的数据生成对应的发票pdf或者合同pdf,这些pdf具有一些特性,就是pdf有固定的格式,类似于表格,我们只要往表格里面填充数据即可。当然,也会涉及到签章,二维码等需求。

总体思路:
1.得到需要生成的pdf的初始模板,包含格式,只是不含数据
2.使用工具Adobe Acrobat,编辑pdf模板,在对应区域生成文本域,每个域都有自己的name
3.java中使用itextpdf对pdf进行操作,将对应的数据填写入pdf

1.使用Adobe Acrobat操作pdf

(1)首先假设我们有一个发票的pdf模板(局部截图)
java使用itextpdf生成pdf并填充自定义数据
(2)下载Adobe Acrobat工具并编辑pdf
我这边使用的是Adobe Acrobat Pro DC,打开pdf后在工具栏中选择准备表单
java使用itextpdf生成pdf并填充自定义数据
我们可以清除默认生成的文本域,按自己的需求进行右键生成文本域大小,这里我生成了一个gfmc的文本域,在这个文本域属性中在外观中可以设置字体的大小,字体的类型;在选项中可以设置文字左对齐,剧中,右对齐,自动换行等。
java使用itextpdf生成pdf并填充自定义数据

2.使用itextpdf操作pdf

当我们对pdf设置完需要的文本域时候,就要对pdf进行一个数据的填充工作。这里展示的是主要的方法代码,并不是整个流程的逻辑代码。
1.获取PdfReader对象

PdfReader reader = new PdfReader(template);//template是模板pdf文件相对路径

2.获取PdfStamper对象,目的是读取模板pdf,写入新的目标文件

File deskFile = new File(filePath);//filePath是将生成的文件
PdfStamper stamp = new PdfStamper(reader, new FileOutputStream(deskFile));

3.获取模板文件中的文本域,并写入数据到新文件中

AcroFields form = stamp.getAcroFields();
form.setField("gfmc", "这是一个购方名称");

4.自定义字体(按需)
(1)加载字体
其中TARGET_COUR_FONT_PATH为字体文件路径

BaseFont courBf = BaseFont.createFont(TARGET_COUR_FONT_PATH, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);

(2)文本域赋值并设置字体
其中chunkStr表示文本内容,size1字体大小,size2行距,property为文本域名称

public static void setFieldAndFont(BaseFont bf, PdfStamper stamp, AcroFields form, String chunkStr, Integer size1, Integer size2, String property) {
        try {
            Font font = new Font(bf, size1,-1,new BaseColor(0,0,0));
            List<AcroFields.FieldPosition> list = form.getFieldPositions(property);

            int page = list.get(0).page;
            PdfContentByte pdfContentByte = stamp.getOverContent(page);
            ColumnText columnText = new ColumnText(pdfContentByte);
            Rectangle rectangle = list.get(0).position;
            columnText.setSimpleColumn(rectangle);
            Chunk chunk = null;
            chunk = new Chunk(chunkStr);
            Paragraph paragraph = new Paragraph(size2, chunk);
            columnText.addText(paragraph);
            paragraph.setFont(font);
            columnText.addElement(paragraph);
            columnText.go();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

5.添加二维码
这里用到的是com.google.zxing

<dependency>
			<groupId>com.google.zxing</groupId>
			<artifactId>core</artifactId>
			<version>3.3.3</version>
		</dependency>
		<dependency>
			<groupId>com.google.zxing</groupId>
			<artifactId>javase</artifactId>
			<version>3.3.3</version>
		</dependency>
public static void invoiceEwm(PdfStamper stamp, String content) throws Exception {
        if(content == null || "".equals(content)){
            return;
        }
        BufferedImage image = createQrCodeBufferdImage(content, 70, 70);

        //PdfGState gs = new PdfGState();
        PdfContentByte waterMar = stamp.getOverContent(1);// 在内容上方加水印
        //waterMar = stamp.getUnderContent(1);//在内容下方加水印
        // 设置图片透明度为0.2f
        //gs.setFillOpacity(0.2f);
        // 设置笔触字体不透明度为0.4f
        //gs.setStrokeOpacity(0.4f);
        // 开始水印处理
        waterMar.beginText();
        // 设置透明度
        //waterMar.setGState(gs);
        // 设置水印字体参数及大小
        //waterMar.setFontAndSize(BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED), 60);
        // 设置水印对齐方式 水印内容 X坐标 Y坐标 旋转角度
        //waterMar.showTextAligned(Element.ALIGN_CENTER, "公司内部文件,请注意保密!",  500, 430, 45);
        // 设置水印颜色
        waterMar.setColorFill(BaseColor.GRAY);
        // 创建水印图片
        //com.itextpdf.text.Image itextimage = getImage(image, 99);
        com.itextpdf.text.Image itextimage = com.itextpdf.text.Image.getInstance(image, new Color(128, 128, 128));
        // 水印图片位置
        itextimage.setAbsolutePosition(150, 380);
        // 边框固定3
        //itextimage.scaleToFit(200, 200);
        // 设置旋转弧度
        //image.setRotation(30);// 旋转 弧度
        // 设置旋转角度
        //image.setRotationDegrees(45);// 旋转 角度
        // 设置等比缩放
        itextimage.scalePercent(85);
        // 自定义大小
        //itextimage.scaleAbsolute(100, 100);
        // 附件加上水印图片
        waterMar.addImage(itextimage);
        // 完成水印添加
        waterMar.endText();
        // stroke
        waterMar.stroke();
    }
    /**
     * 生成二维码
     * @author swj
     * @date   2019年6月11日下午4:39:16
     * @param contents 二维码的内容
     * @param width 二维码图片宽度
     * @param height 二维码图片高度
     */
public static BufferedImage createQrCodeBufferdImage(String contents, int width, int height){
        Hashtable hints= new Hashtable();
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        hints.put(EncodeHintType.MARGIN, 0);
        BufferedImage image = null;
        try {
            BitMatrix bitMatrix = new MultiFormatWriter().encode(
                    contents, BarcodeFormat.QR_CODE, width, height, hints);
            image = toBufferedImage(bitMatrix);
        } catch (WriterException e) {
            e.printStackTrace();
        }
        return image;
    }

6.添加签章(盖章)
在指定位置添加图片

private static void invoiceDefaultSign(PdfStamper stamper, String filePath, int x, int y) throws Exception {

        Resource resource = new ClassPathResource(filePath);
        byte[] bytes = null;
        bytes = FileUtils.is2ByeteArray(resource.getInputStream());

        // 读图片
        Image image = Image.getInstance(bytes);
        // 获取操作的页面
        PdfContentByte under = stamper.getOverContent(1);
        // 根据域的大小缩放图片
        image.scaleToFit(MAIN_SIGN_WIDTH, MAIN_SIGN_HEIGHT);
        // 添加图片
        image.setAbsolutePosition(x, y);
        under.addImage(image);

    }
/**
	 * 通过InputStream 转Byte数组
	 *
	 * @param is
	 * @return
	 * @throws IOException
	 */
	public static byte[] is2ByeteArray(InputStream is) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		byte[] buff = new byte[100];
		int rc = 0;
		while((rc=is.read(buff, 0, 100))>0) {
			baos.write(buff, 0, rc);
		}

		return baos.toByteArray();
	}

7.关闭流
操作完成后,记得在最后关闭流,很简单,但是很重要

finally {
            if (stamp != null) {
                stamp.close();
            }
            if (reader != null) {
                reader.close();
            }
        }

写在最后

如果对pdf的文本域位置要求比较高的,需要逐步的进行微调,还是比较考验耐心的。涉及到数据的安全,一些效果图我就不上传了。如果生成了多个pdf,需要对pdf进行一个合并的操作,最后可以根据需求,上传至公司fastdfs文件服务器等操作。

相关标签: itextpdf java