笔记整理来源https://blog.csdn.net/tomatocc 仅供学习参考
一、创建一个简单的PDF文件
package com.hxc;
import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.FileOutputStream;
public class PdfTest {
public static void main(String[] args) throws Exception {
//第一步:创建一个文档实例 设置文档纸张为A4,文档排列为横向排列
//实现A4纸页面 并且纵向排列(不设置则为横向)
Document document = new Document(PageSize.A4.rotate());
// 第二步:创建PdfWriter对象,设置pdf生成路径
PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream("E:\\java\\Project\\KuangShen\\PdfTest\\demo1.pdf"));
// 第三步:打开文档进行我们需要的操作
document.open();
//第四步:创建第一页(如果只有一页,可以省略这一步)
document.newPage();
// 第五步:在文档中添加内容
document.add(new Paragraph("this is my first pdf demo"));
//第六步:关闭文档
document.close();
//第七步:关闭书写流
pdfWriter.close();
}
}
二、设置PDF一些常用属性
package com.hxc;
import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.FileOutputStream;
public class PdfTest2 {
public static void main(String[] args) throws Exception {
//实现A4纸页面并且横向显示(不设置则为 纵向)
Document document = new Document(PageSize.A4.rotate());
PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream("E:\\java\\Project\\KuangShen\\PdfTest\\demo2.pdf"));
//打开文档
document.open();
//生成第一页
document.newPage();
//设置作者
document.addAuthor("hxc");
//设置创建日期
document.addCreationDate();
//设置创建者
document.addCreator("by hxc");
//设置生产者
document.addProducer();
//设置关键字
document.addKeywords("me");
//设置标题
document.addTitle("set pdf properties");
//设置主题
document.addSubject("pdf properties");
//加入文档内容
document.add(new Paragraph("my first pdf demo"));
//关闭文档
document.close();
pdfWriter.close();
}
}
右键文件属性即可查看刚刚设置的PDF文件属性
三、对PDF文档进行加密和权限设置
itext中对pdf文档的加密包括两部分,第一部分是用户密码,第二部分是所有者密码。这两部分可以简单的理解为管理员密码和用户密码,因此我们在设置这两个密码的权限的时候,往往会将所有者密码的权限级别设置的更高,而用户密码权限往往是“只读”。
引入依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.54</version>
</dependency>
package com.hxc;
import com.itextpdf.text.Document;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.FileOutputStream;
public class PdfTest3 {
public static void main(String[] args) throws Exception {
//实现A4纸页面 并且横向显示(不设置则为纵向)
Document document = new Document(PageSize.A4.rotate());
PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream("E:\\java\\Project\\KuangShen\\PdfTest\\demo3.pdf"));
// 设置用户密码, 所有者密码,用户权限,所有者权限
pdfWriter.setEncryption("userPassword".getBytes(),"ownerPassword".getBytes(), PdfWriter.ALLOW_COPY, PdfWriter.ENCRYPTION_AES_128);
//打开文档
document.open();
//创建第一页(如果只有一页,这一步可以省略)
document.newPage();
//加入文档内容
document.add(new Paragraph("pdf permission"));
//关闭文档
document.close();
}
}
权限参数
权限名称 | 权限 | 加密级别 |
---|---|---|
PdfWriter.ALLOW_MODIFY_CONTENTS | 允许打印,编辑,复制,签名 | 40-bit-RC4 |
PdfWriter.ALLOW_COPY | 允许复制,签名 不允许打印,编辑 | 40-bit-RC |
PdfWriter.ALLOW_MODIFY_ANNOTATIONS | 允许打印,编辑,复制,签名 | 40-bit-RC4 |
PdfWriter.ALLOW_FILL_IN | 允许打印,编辑,复制,签名 | 40-bit-RC4 |
PdfWriter.ALLOW_SCREENREADERS | 允许打印,编辑,复制,签名 | 40-bit-RC4 |
PdfWriter.ALLOW_ASSEMBLY | 允许打印,编辑,复制,签名 | 40-bit-RC4 |
PdfWriter.EMBEDDED_FILES_ONLY | 允许打印,编辑,复制,签名 | 0-bit-RC4 |
PdfWriter.DO_NOT_ENCRYPT_METADATA | 允许打印,编辑,复制,签名 | 40-bit-RC4 |
PdfWriter.ENCRYPTION_AES_256 | 允许打印,编辑,复制,签名 | 256-bit-AES |
PdfWriter.ENCRYPTION_AES_128 | 允许打印,编辑,复制,签名 | 128-bit-AES |
PdfWriter.STANDARD_ENCRYPTION_128 | 允许打印,编辑,复制,签名 | 128-bit-RC4 |
PdfWriter.STANDARD_ENCRYPTION_40 | 允许打印,编辑,复制,签名 | 40-bit-RC4 |
四、对PDF加文字或图片水印
1、文字水印
package com.hxc;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfGState;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.FileOutputStream;
public class PdfTest4 {
public static void main(String[] args) throws Exception {
//实现A4纸页面 并且横向显示(不设置则为纵向)
Document document = new Document(PageSize.A4.rotate());
PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream("E:\\java\\Project\\KuangShen\\PdfTest\\demo4.pdf"));
// 打开文档
document.open();
// 创建第一页(如果只有一页的话,这一步可以省略)
document.newPage();
//加入水印
PdfContentByte waterMar = pdfWriter.getDirectContentUnder();
//开始设置水印
waterMar.beginText();
//设置水印透明度
PdfGState gs = new PdfGState();
//设置填充字体不透明度为0.4f
gs.setFillOpacity(0.4f);
try{
//设置水印字体参数及大小 (字体参数,字体编码格式,是否将字体信息嵌入到PDF中(一般不需要嵌入),字体大小
waterMar.setFontAndSize(BaseFont.createFont(BaseFont.HELVETICA,BaseFont.WINANSI,BaseFont.NOT_EMBEDDED),60);
//设置透明度
waterMar.setGState(gs);
//设置水印对齐方式,水印方式 x坐标 y坐标 旋转角度
waterMar.showTextAligned(Element.ALIGN_RIGHT,"www.ppp.com",400,350,50);
//设置水印颜色
waterMar.setColorFill(BaseColor.GRAY);
//结束设置
waterMar.endText();
waterMar.stroke();
}catch (Exception e){
e.printStackTrace();
}finally {
waterMar=null;
gs=null;
}
//加入文档内容
document.add(new Paragraph("PDF Watermarking Test "));
//关闭文档
document.close();
pdfWriter.close();
}
}
这里要说明一点:水印是要放在pdf中的,因此在创建PdfContentByte对象之前,一定要先 document.newPage();。二者顺序是不能反过来的,如果反过来的意思就是先画水印,在创建一个pdf页面,显然不符合逻辑了。
2、图片水印
package com.hxc;
import com.itextpdf.text.Document;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfGState;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
* 图片水印
*/
public class PdfTest5 {
public static void main(String[] args) throws Exception {
//实现A4纸页面 并且横向显示(不设置则为纵向)
Document document = new Document(PageSize.A4.rotate());
PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream("E:\\java\\Project\\KuangShen\\PdfTest\\demo5.pdf"));
// 打开文档
document.open();
// 创建第一页(如果只有一页的话,这一步可以省略)
document.newPage();
// 加入水印
PdfContentByte waterMar = pdfWriter.getDirectContentUnder();
//开始设置水印
waterMar.beginText();
// 设置水印透明度
PdfGState gs = new PdfGState();
// 设置笔触字体不透明度为0.4f
gs.setStrokeOpacity(0.4f);
try {
Image image = Image.getInstance("D:\\testCertFile\\测试印章.gif");
//设置坐标 绝对位置X Y
image.setAbsolutePosition(300,100);
//设置旋转弧度
image.setRotation(30); //旋转 幅度
// 设置旋转角度
image.setRotationDegrees(45); //旋转 角度
//设置等比缩放
image.scalePercent(90); //依照比例缩放
// image.scaleAbsolute(200,100);//自定义大小
// 设置透明度
waterMar.setGState(gs);
//添加水印图片
waterMar.addImage(image);
//设置透明度
waterMar.setGState(gs);
//结束设置
waterMar.endText();
waterMar.stroke();
}catch (Exception e){
e.printStackTrace();
}finally {
waterMar = null;
gs = null;
}
//加入文档内容
document.add(new Paragraph("PDF picture watermarking test"));
// 关闭文档
document.close();
pdfWriter.close();
}
}
五、对PDF进行盖章/签章/数字签名
在本文中,我们需要生成一个.p12结尾的数字证书,该证书用来对我们的pdf文档进行数字签名。生成.p12证书的方法请参考我的另一篇文章:使用JDK的keytool生成p12证书
导入依赖:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.52</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.54</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.10</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
第一步:创建SignatureInfo的实体类,用途是为了方便的增加需要签章的信息
package com.hxc.entity;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import lombok.Data;
import java.security.PrivateKey;
import java.security.cert.Certificate;
@Data
public class SignatureInfo {
private String reason; //签名的原因,显示在pdf签名属性中
private String location;//签名的地点,显示在pdf签名属性中
private String digestAlgorithm;//摘要算法名称,例如SHA-1
private String imagePath;//图章路径
private String fieldName;//表单域名称
private Certificate[] chain;//证书链
private PrivateKey pk;//签名私钥
private int certificationLevel = 0; //批准签章
private PdfSignatureAppearance.RenderingMode renderingMode;//表现形式:仅描述,仅图片,图片和描述,签章者和描述
//图章属性
private float rectllx ;//图章左下角x
private float rectlly ;//图章左下角y
private float recturx ;//图章右上角x
private float rectury ;//图章右上角y
}
第二步:我们创建一个用于对pdf签章的工具类,以及main方法
package com.hxc.util;
import com.hxc.entity.SignatureInfo;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.*;
import java.io.*;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
public class ItextUtil {
public static final char[] PASSWORD = "123456".toCharArray();// keystory密码
/**
* 单多次章通用
* @param src
* @param target
* @param signatureInfo
*/
@SuppressWarnings("resource")
public void sign(String src, String target, SignatureInfo signatureInfo) {
InputStream inputStream = null;
FileOutputStream outputStream = null;
ByteArrayOutputStream result = new ByteArrayOutputStream();
try {
inputStream = new FileInputStream(src);
ByteArrayOutputStream tempArrayOutputStream = new ByteArrayOutputStream();
PdfReader reader = new PdfReader(inputStream);
// 创建签章工具PdfStamper ,最后一个boolean参数是否允许被追加签名
// false的话,pdf文件只允许被签名一次,多次签名,最后一次有效
// true的话,pdf可以被追加签名,验签工具可以识别出每次签名之后文档是否被修改
PdfStamper stamper = PdfStamper.createSignature(reader,
tempArrayOutputStream, '\0', null, true);
// 获取数字签章属性对象
PdfSignatureAppearance appearance = stamper
.getSignatureAppearance();
appearance.setReason(signatureInfo.getReason());
appearance.setLocation(signatureInfo.getLocation());
// 设置签名的位置,页码,签名域名称,多次追加签名的时候,签名预名称不能一样 图片大小受表单域大小影响(过小导致压缩)
// 签名的位置,是图章相对于pdf页面的位置坐标,原点为pdf页面左下角
// 四个参数的分别是,图章左下角x,图章左下角y,图章右上角x,图章右上角y
appearance.setVisibleSignature(
new Rectangle(signatureInfo.getRectllx(), signatureInfo
.getRectlly(), signatureInfo.getRecturx(),
signatureInfo.getRectury()), 1, signatureInfo
.getFieldName());
// 读取图章图片
Image image = Image.getInstance(signatureInfo.getImagePath());
appearance.setSignatureGraphic(image);
appearance.setCertificationLevel(signatureInfo
.getCertificationLevel());
// 设置图章的显示方式,如下选择的是只显示图章(还有其他的模式,可以图章和签名描述一同显示)
appearance.setRenderingMode(signatureInfo.getRenderingMode());
// 摘要算法
ExternalDigest digest = new BouncyCastleDigest();
// 签名算法
ExternalSignature signature = new PrivateKeySignature(
signatureInfo.getPk(), signatureInfo.getDigestAlgorithm(),
null);
// 调用itext签名方法完成pdf签章 //数字签名格式,CMS,CADE
MakeSignature.signDetached(appearance, digest, signature,
signatureInfo.getChain(), null, null, null, 0,
MakeSignature.CryptoStandard.CADES);
inputStream = new ByteArrayInputStream(
tempArrayOutputStream.toByteArray());
// 定义输入流为生成的输出流内容,以完成多次签章的过程
result = tempArrayOutputStream;
outputStream = new FileOutputStream(new File(target));
outputStream.write(result.toByteArray());
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != outputStream) {
outputStream.close();
}
if (null != inputStream) {
inputStream.close();
}
if (null != result) {
result.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
try {
ItextUtil app = new ItextUtil();
// 将证书文件放入指定路径,并读取keystore ,获得私钥和证书链
String pkPath = "D:\\itext\\tomatocc.p12";
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(pkPath), PASSWORD);
String alias = ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);
// 得到证书链
Certificate[] chain = ks.getCertificateChain(alias);
//需要进行签章的pdf
String path = "D:\\itext\\demo.pdf";
// 封装签章信息
SignatureInfo signInfo = new SignatureInfo();
signInfo.setReason("理由");
signInfo.setLocation("位置");
signInfo.setPk(pk);
signInfo.setChain(chain);
signInfo.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED);
signInfo.setDigestAlgorithm(DigestAlgorithms.SHA1);
signInfo.setFieldName("demo");
// 签章图片
signInfo.setImagePath("D:\\itext\\sign.jpg");
signInfo.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);
signInfo.setRectllx(100); // 值越大,代表向x轴坐标平移 缩小 (反之,值越小,印章会放大)
signInfo.setRectlly(200); // 值越大,代表向y轴坐标向上平移(大小不变)
signInfo.setRecturx(400); // 值越大 代表向x轴坐标向右平移 (大小不变)
signInfo.setRectury(100); // 值越大,代表向y轴坐标向上平移(大小不变)
//签章后的pdf路径
app.sign(path, "D:\\itext\\demo3.pdf", signInfo);
} catch (Exception e) {
e.printStackTrace();
}
}
}
**注意:**demo.pdf不可以直接通过改后缀名转换,否则会报PDF header signature not found.错误
`