参考:
具体步骤: 由PDF模板生成一个PDF文件、加签章。由itext5 生成的签章 图片更加清晰
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.AcroFields.Item;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
public class PDFUtils {
/**
* @param fields
* @param data
* @throws IOException
* @throws DocumentException
*/
private static void fillData(AcroFields fields, Map<String, String> data) throws IOException, DocumentException {
List<String> keys = new ArrayList<String>();
Map<String, Item> formFields = fields.getFields();
for (String key : data.keySet()) {
if(formFields.containsKey(key)){
String value = data.get(key);
fields.setField(key, value); // 为字段赋值,注意字段名称是区分大小写的
keys.add(key);
}
}
Iterator<String> itemsKey = formFields.keySet().iterator();
while(itemsKey.hasNext()){
String itemKey = itemsKey.next();
if(!keys.contains(itemKey)){
fields.setField(itemKey, " ");
}
}
}
/**
* @param templatePdfPath
* 模板pdf路径
* @param generatePdfPath
* 生成pdf路径
* @param data
* 数据
*/
public static String generatePDF(String templatePdfPath, String generatePdfPath, Map<String, String> data) {
OutputStream fos = null;
ByteArrayOutputStream bos = null;
PdfReader reader=null;
PdfStamper ps =null;
try {
reader = new PdfReader(templatePdfPath);
bos = new ByteArrayOutputStream();
/* 将要生成的目标PDF文件名称 */
ps = new PdfStamper(reader, bos);
/* 使用中文字体 */
BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED);
ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
fontList.add(bf);
/* 取出报表模板中的所有字段 */
AcroFields fields = ps.getAcroFields();
fields.setSubstitutionFonts(fontList);
fillData(fields, data);
/* 必须要调用这个,否则文档不会生成的 如果为false那么生成的PDF文件还能编辑,一定要设为true*/
ps.setFormFlattening(true);
ps.close();
fos = new FileOutputStream(generatePdfPath);
fos.write(bos.toByteArray());
fos.flush();
return generatePdfPath;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public static void main(String[] args) {
Map<String, String> data = new HashMap<String, String>();
//key为pdf模板的form表单的名字,value为需要填充的值
data.put("title", "在线医院");
data.put("case", "123456789");
data.put("date", "2018.12.07");
data.put("name", "gitbook");
data.put("sex", "男");
data.put("age", "29");
data.put("phone", "13711645814");
data.put("office", "内科");
data.put("cert", "身痒找打");
data.put("drug", "1、奥美拉唑肠溶胶囊 0.25g10粒×2板 ");
data.put("dose", "×2盒");
data.put("cons", "用法用量:口服 一日两次 一次2粒");
data.put("tips", "温馨提示");
data.put("desc", "尽量呆在通风较好的地方,保持空气流通,有利于病情康复。尽量呆在通风较好的地方");
generatePDF("C:\\Users\\zhilin\\Desktop\\chat\\tpl.pdf",
"C:\\Users\\zhilin\\Desktop\\chat\\filled.pdf", data );
}
}
经过上面的代码可以生成一个名为sign.jpg的签章图片,生成一个keystore.p12的证书文件,还有一个已经通过模板填充了表单的名为filled.pdf的pdf文件。下面就可通过以上材料生成一个签名的PDF文件。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.UUID;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
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.PdfSignatureAppearance.RenderingMode;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.PrivateKeySignature;
public class SignPdf {
/**
* @param password 秘钥密码
* @param keyStorePath 秘钥文件路径
* @param signPdfSrc 签名的PDF文件
* @param signImage 签名图片文件
* @param x x坐标
* @param y y坐标
* @return
*/
public static byte[] sign(String password, String keyStorePath, String signPdfSrc, String signImage, float x, float y) {
File signPdfSrcFile = new File(signPdfSrc);
PdfReader reader = null;
ByteArrayOutputStream signPDFData = null;
PdfStamper stp = null;
FileInputStream fos = null;
try {
// 加入算法提供者
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
KeyStore ks = KeyStore.getInstance("PKCS12", new BouncyCastleProvider());
fos = new FileInputStream(keyStorePath);
// 私钥密码 为Pkcs生成证书是的私钥密码 123456
ks.load(fos, password.toCharArray());
String alias = (String) ks.aliases().nextElement();
// 获取私钥
PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
// 获取证书链
Certificate[] chain = ks.getCertificateChain(alias);
reader = new PdfReader(signPdfSrc);
signPDFData = new ByteArrayOutputStream();
// 临时pdf文件
File temp = new File(signPdfSrcFile.getParent(), System.currentTimeMillis() + ".pdf");
stp = PdfStamper.createSignature(reader, signPDFData, '\0', temp, true);
stp.setFullCompression();
PdfSignatureAppearance sap = stp.getSignatureAppearance();
sap.setReason("数字签名,不可改变");
// 使用png格式透明图片
Image image = Image.getInstance(signImage);
sap.setImageScale(0);
sap.setSignatureGraphic(image);
sap.setRenderingMode(RenderingMode.GRAPHIC);
// 是对应x轴和y轴坐标
sap.setVisibleSignature( new Rectangle(x, y, x + 185, y + 68), 1, UUID.randomUUID().toString().replaceAll("-", ""));
stp.getWriter().setCompressionLevel(5);
ExternalDigest digest = new BouncyCastleDigest();
ExternalSignature signature = new PrivateKeySignature(key, DigestAlgorithms.SHA512, provider.getName());
MakeSignature.signDetached(sap, digest, signature, chain, null, null, null, 0, CryptoStandard.CADES);
stp.close();
reader.close();
return signPDFData.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (signPDFData != null) {
try {
signPDFData.close();
} catch (IOException e) {
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
}
}
}
return null;
}
public static void main(String[] args) throws Exception {
byte[] fileData = sign("123456", "C:\\Users\\zhilin\\Desktop\\chat\\keystore.p12", //
"C:\\Users\\zhilin\\Desktop\\chat\\filled.pdf",//
"C:\\Users\\zhilin\\Desktop\\chat\\sign.jpg", 100, 290);
FileOutputStream f = new FileOutputStream(new File("C:\\Users\\zhilin\\Desktop\\chat\\signed.pdf"));
f.write(fileData);
f.close();
}
}
高清签章是通过iText的绘制功能来完成。主要直接在PDF文件中绘制签章,代码实现如下:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import com.itextpdf.awt.AsianFontMapper;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfSignatureAppearance;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfStream;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.security.BouncyCastleDigest;
import com.itextpdf.text.pdf.security.DigestAlgorithms;
import com.itextpdf.text.pdf.security.ExternalDigest;
import com.itextpdf.text.pdf.security.ExternalSignature;
import com.itextpdf.text.pdf.security.MakeSignature;
import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;
import com.itextpdf.text.pdf.security.PrivateKeySignature;
public class SignHighPdf {
/**
* @param password
* 秘钥密码
* @param keyStorePath
* 秘钥文件路径
* @param signPdfSrc
* 签名的PDF文件
* @param x
*
* @param y
* @return
*/
public static byte[] sign(String password, String keyStorePath, String signPdfSrc, float x, float y, String signText) {
File signPdfSrcFile = new File(signPdfSrc);
PdfReader reader = null;
ByteArrayOutputStream signPDFData = null;
PdfStamper stp = null;
FileInputStream fos = null;
try {
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
KeyStore ks = KeyStore.getInstance("PKCS12", new BouncyCastleProvider());
fos = new FileInputStream(keyStorePath);
ks.load(fos, password.toCharArray()); // 私钥密码
String alias = (String) ks.aliases().nextElement();
PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
reader = new PdfReader(signPdfSrc);
signPDFData = new ByteArrayOutputStream();
// 临时pdf文件
File temp = new File(signPdfSrcFile.getParent(), System.currentTimeMillis() + ".pdf");
stp = PdfStamper.createSignature(reader, signPDFData, '\0', temp, true);
PdfSignatureAppearance sap = stp.getSignatureAppearance();
sap.setReason("数字签名,不可改变");
// 是对应x轴和y轴坐标
sap.setVisibleSignature(new Rectangle(x, y, x + 150, y + 65), 1, "sr"+String.valueOf(System.nanoTime()));
/////////////////layer 0 Creating the appearance for layer 0
PdfTemplate n0 = sap.getLayer(0);
n0.reset();
float lx = n0.getBoundingBox().getLeft();
float by = n0.getBoundingBox().getBottom();
float width = n0.getBoundingBox().getWidth();
float height = n0.getBoundingBox().getHeight();
n0.setRGBColorFill(255, 0, 0);
n0.rectangle(lx, by, 5, height);
n0.rectangle(lx, by, width, 5);
n0.rectangle(lx, by+height-5, width, 5);
n0.rectangle(lx+width-5, by, 5, height);
n0.fill();
///////////////////////layer 2
PdfTemplate n2 = sap.getLayer(2);
n2.setCharacterSpacing(0.0f);
ColumnText ct = new ColumnText(n2);
ct.setSimpleColumn(n2.getBoundingBox());
n2.setRGBColorFill(255, 0, 0);
//做一个占位的动作
Paragraph p1 = new Paragraph(" ");
BaseFont bf = BaseFont.createFont(
AsianFontMapper.ChineseSimplifiedFont,
AsianFontMapper.ChineseSimplifiedEncoding_H,
BaseFont.NOT_EMBEDDED);
Font font1 = new Font(bf, 5, Font.BOLD, BaseColor.RED);
Font font2 = new Font(bf, 13, Font.BOLD, BaseColor.RED);
p1.setFont(font1);
ct.addElement(p1);
Paragraph p = new Paragraph(signText);
p.setAlignment(Element.ALIGN_CENTER);
p.setFont(font2);
ct.addElement(p);
ct.go();
stp.getWriter().setCompressionLevel(PdfStream.BEST_COMPRESSION);
ExternalDigest digest = new BouncyCastleDigest();
ExternalSignature signature = new PrivateKeySignature(key, DigestAlgorithms.SHA512, provider.getName());
MakeSignature.signDetached(sap, digest, signature, chain, null, null, null, 0, CryptoStandard.CADES);
stp.close();
reader.close();
return signPDFData.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (signPDFData != null) {
try {
signPDFData.close();
} catch (IOException e) {
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
}
}
}
return null;
}
public static void main(String[] args) throws Exception {
//对已经签章的signed.pdf文件再次签章,这次是高清签章
byte[] fileData = sign("123456", "C:\\Users\\zhilin\\Desktop\\chat\\keystore.p12",//
"C:\\Users\\zhilin\\Desktop\\chat\\signed.pdf", 350, 290, "华佗\n2017-12-20");
FileOutputStream f = new FileOutputStream(new File("C:\\Users\\zhilin\\Desktop\\chat\\signed2.pdf"));
f.write(fileData);
f.close();
}
}