package cn.report.pdf.sign.demo;


import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.StampingProperties;
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.renderer.CellRenderer;
import com.itextpdf.layout.renderer.DrawContext;
import com.itextpdf.signatures.*;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;

//演示：工作流程中由多个人在另外创建的页面上对PDF进行数字签名
public class SignatureWorkflow 
{
	public static final String FORM = "D:/test/form.pdf";
	public static final String ALICE = "D:/test/encryption/alice";
	
	public static final String BOB = "D:/test/encryption/bob";
	public static final String CAROL = "D:/test/encryption/carol";
	public static final String DAVE = "D:/test/encryption/dave";
	public static final String KEYSTORE = "D:/test/encryption/ks";
	public static final char[] PASSWORD = "password".toCharArray();
	public static final String DEST = "D:/test/step%s_signed_by_%s.pdf";

	boolean isAppendMode = false;//true不输出;

	public static void main(String[] args) throws IOException, GeneralSecurityException {
		BouncyCastleProvider provider = new BouncyCastleProvider();
		Security.addProvider(provider);
		SignatureWorkflow app = new SignatureWorkflow();
		
		app.createForm();//创建PDF文件
		app.certify(ALICE, FORM, "sig1", String.format(DEST, 1, "alice"));
		
		//阅读者
		app.fillOut(String.format(DEST, 1, "alice"), String.format(DEST, 2, "alice_and_filled_out_by_bob"), "approved_bob", "Read and Approved by Bob");
		//阅读者签字
		app.sign(BOB, String.format(DEST, 2, "alice_and_filled_out_by_bob"), "sig2", String.format(DEST, 3, "alice_and_bob"));
		
		app.fillOut(String.format(DEST, 3, "alice_and_bob"), String.format(DEST, 4, "alice_and_bob_filled_out_by_carol"), "approved_carol", "Read and Approved by Carol");
		app.sign(CAROL, String.format(DEST, 4, "alice_and_bob_filled_out_by_carol"), "sig3", String.format(DEST, 5, "alice_bob_and_carol"));
		
		//app.fillOutAndSign(DAVE, String.format(DEST, 5, "alice_bob_and_carol"), "sig4", "approved_dave", "Read and Approved by Dave", String.format(DEST, 6, "alice_bob_carol_and_dave"));
	}

	public void createForm() throws IOException {
		PdfDocument pdfDoc = new PdfDocument(new PdfWriter(FORM));
		Document doc = new Document(pdfDoc);
		Table table = new Table(1);
		table.addCell("Written by Alice");
		table.addCell(createSignatureFieldCell("sig1"));
		table.addCell("For approval by Bob");
		table.addCell(createTextFieldCell("approved_bob"));
		table.addCell(createSignatureFieldCell("sig2"));
		table.addCell("For approval by Carol");
		table.addCell(createTextFieldCell("approved_carol"));
		table.addCell(createSignatureFieldCell("sig3"));
		table.addCell("For approval by Dave");
		table.addCell(createTextFieldCell("approved_dave"));
		table.addCell(createSignatureFieldCell("sig4"));
		doc.add(table);
		doc.close();
	}

	public void certify(String keystore, String src, String name, String dest) throws GeneralSecurityException, IOException 
	{
	    //返回 java 安全属性文件中指定的默认 keystore 类型；如果不存在此类属性，则返回字符串 "jks"（"java keystore" 的首字母缩写）。
		//java 安全属性文件位于名为 /lib/security/java.security 的文件中。
		KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
		ks.load(new FileInputStream(keystore), PASSWORD);
		String alias = ks.aliases().nextElement();
		PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);
		Certificate[] chain = ks.getCertificateChain(alias);
		/// Creating the reader and the signer
		PdfReader reader = new PdfReader(src);
		
		StampingProperties properties = new StampingProperties();
		if (isAppendMode){//Defines if the document will be edited in append mode.
			properties.useAppendMode();
		}
		PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest),properties);
		// PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), true);
		
		// Setting signer options
		signer.setFieldName(name);
		// TODO DEVSIX-488
		signer.setCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING);
		
		// Creating the signature
		IExternalSignature pks = new PrivateKeySignature(pk, "SHA-256", "BC");
		IExternalDigest digest = new BouncyCastleDigest();
		//Signs the document using the detached mode, CMS or CAdES equivalent.
		signer.signDetached(digest, pks, chain, null, null, null, 0, PdfSigner.CryptoStandard.CMS);
	}

	public void fillOut(String src, String dest, String name, String value) throws IOException 
	{
		PdfDocument pdfDoc = new PdfDocument(new PdfReader(src), new PdfWriter(dest), new StampingProperties().useAppendMode());
		PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
		form.getField(name).setValue(value);
		form.getField(name).setReadOnly(true);
		pdfDoc.close();
	}

	public void sign(String keystore, String src, String name, String dest)	throws GeneralSecurityException, IOException {
		KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
		ks.load(new FileInputStream(keystore), PASSWORD);
		String alias = ks.aliases().nextElement();
		PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);
		Certificate[] chain = ks.getCertificateChain(alias);
		// Creating the reader and the signer
		PdfReader reader = new PdfReader(src);
		
		StampingProperties properties = new StampingProperties();
		if (isAppendMode){//Defines if the document will be edited in append mode.
			properties.useAppendMode();
		}
		PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest),properties);
		// PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), true);
		
		// Setting signer options
		signer.setFieldName(name);
		// Creating the signature
		IExternalSignature pks = new PrivateKeySignature(pk, "SHA-256", "BC");
		IExternalDigest digest = new BouncyCastleDigest();
		signer.signDetached(digest, pks, chain, null, null, null, 0, PdfSigner.CryptoStandard.CMS);
	}

	public void fillOutAndSign(String keystore, String src, String name, String fname, String value, String dest)
			throws GeneralSecurityException, IOException {
		KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
		ks.load(new FileInputStream(keystore), PASSWORD);
		String alias = ks.aliases().nextElement();
		PrivateKey pk = (PrivateKey) ks.getKey(alias, PASSWORD);
		Certificate[] chain = ks.getCertificateChain(alias);
		// Creating the reader and the signer
		PdfReader reader = new PdfReader(src);
		
		StampingProperties properties = new StampingProperties();
		if (isAppendMode){//Defines if the document will be edited in append mode.
			properties.useAppendMode();
		}
		PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest),properties);
		// PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), true);
		
		PdfAcroForm form = PdfAcroForm.getAcroForm(signer.getDocument(), true);
		form.getField(fname).setValue(value);
		form.getField(fname).setReadOnly(true);
		// Setting signer options
		signer.setFieldName(name);
		// Creating the signature
		IExternalSignature pks = new PrivateKeySignature(pk, "SHA-256", "BC");
		IExternalDigest digest = new BouncyCastleDigest();
		signer.signDetached(digest, pks, chain, null, null, null, 0, PdfSigner.CryptoStandard.CMS);
	}

	protected Cell createTextFieldCell(String name) {
		Cell cell = new Cell();
		cell.setHeight(20);
		cell.setNextRenderer(new TextFieldCellRenderer(cell, name));
		return cell;
	}

	protected Cell createSignatureFieldCell(String name) {
		Cell cell = new Cell();
		cell.setHeight(50);
		cell.setNextRenderer(new SignatureFieldCellRenderer(cell, name));
		return cell;
	}


	class TextFieldCellRenderer extends CellRenderer {
		public String name;

		public TextFieldCellRenderer(Cell modelElement, String name) {
			super(modelElement);
			this.name = name;
		}

		@Override
		public void draw(DrawContext drawContext) {
			super.draw(drawContext);
			PdfFormField field = PdfFormField.createText(drawContext.getDocument(), getOccupiedAreaBBox(), name);
			PdfAcroForm.getAcroForm(drawContext.getDocument(), true).addField(field);
		}
	}


	class SignatureFieldCellRenderer extends CellRenderer {
		public String name;

		public SignatureFieldCellRenderer(Cell modelElement, String name) {
			super(modelElement);
			this.name = name;
		}

		@Override
		public void draw(DrawContext drawContext) {
			super.draw(drawContext);
			PdfFormField field = PdfFormField.createSignature(drawContext.getDocument(), getOccupiedAreaBBox());
			field.setFieldName(name);
			field.getWidgets().get(0).setHighlightMode(PdfAnnotation.HIGHLIGHT_INVERT);
			field.getWidgets().get(0).setFlags(PdfAnnotation.PRINT);
			PdfAcroForm.getAcroForm(drawContext.getDocument(), true).addField(field);
		}
	}

}
