PDFBOX-1766:
- visible signature generator
- example
- new method in the SignatureOptions for the new PDVisibleSigProperties
git-svn-id: https://svn.apache.org/repos/asf/pdfbox/trunk@1541625 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java b/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java
new file mode 100644
index 0000000..9523ae7
--- /dev/null
+++ b/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateVisibleSignature.java
@@ -0,0 +1,266 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.examples.signature;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.pdfbox.exceptions.COSVisitorException;
+import org.apache.pdfbox.exceptions.SignatureException;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSigProperties;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSignDesigner;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.CMSSignedGenerator;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+/**
+ * <p>
+ * This is an example for signing a pdf with bouncy castle.
+ * </p>
+ * <p>
+ * And also you can create visible signature too
+ * </p>
+ * <p>
+ * A keystore can be created with the java keytool (e.g. keytool -genkeypair -storepass 123456 -storetype pkcs12 -alias
+ * test -validity 365 -v -keyalg RSA -keystore keystore.p12 )
+ * </p>
+ *
+ * @author Vakhtang koroghlishvili (Gogebashvili)
+ */
+public class CreateVisibleSignature implements SignatureInterface
+{
+
+ private static BouncyCastleProvider provider = new BouncyCastleProvider();
+
+ private PrivateKey privKey;
+
+ private Certificate[] cert;
+
+ private SignatureOptions options;
+
+ /**
+ * Initialize the signature creator with a keystore (pkcs12) and pin that
+ * should be used for the signature.
+ *
+ * @param keystore
+ * is a pkcs12 keystore.
+ * @param pin
+ * is the pin for the keystore / private key
+ */
+ public CreateVisibleSignature(KeyStore keystore, char[] pin)
+ {
+ try {
+ /*
+ * grabs the first alias from the keystore and get the private key. An
+ * alternative method or constructor could be used for setting a specific
+ * alias that should be used.
+ */
+ Enumeration<String> aliases = keystore.aliases();
+ String alias = null;
+ if (aliases.hasMoreElements()) {
+ alias = aliases.nextElement();
+ } else {
+ throw new RuntimeException("Could not find alias");
+ }
+ privKey = (PrivateKey) keystore.getKey(alias, pin);
+ cert = keystore.getCertificateChain(alias);
+ } catch (KeyStoreException e) {
+ e.printStackTrace();
+ } catch (UnrecoverableKeyException e) {
+ System.err.println("Could not extract private key.");
+ e.printStackTrace();
+ } catch (NoSuchAlgorithmException e) {
+ System.err.println("Unknown algorithm.");
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Signs the given pdf file.
+ *
+ * @param document is the pdf document
+ * @param signatureProperties
+ * @return the signed pdf document
+ * @throws IOException
+ * @throws COSVisitorException
+ * @throws SignatureException
+ */
+ public File signPDF(File document, PDVisibleSigProperties signatureProperties) throws IOException,
+ COSVisitorException, SignatureException
+ {
+ byte[] buffer = new byte[8 * 1024];
+ if (document == null || !document.exists()) {
+ new RuntimeException("Document for signing does not exist");
+ }
+
+ // creating output document and prepare the IO streams.
+ String name = document.getName();
+ String substring = name.substring(0, name.lastIndexOf("."));
+
+ File outputDocument = new File(document.getParent(), substring + "_signed.pdf");
+ FileInputStream fis = new FileInputStream(document);
+ FileOutputStream fos = new FileOutputStream(outputDocument);
+
+ int c;
+ while ((c = fis.read(buffer)) != -1) {
+ fos.write(buffer, 0, c);
+ }
+ fis.close();
+ fis = new FileInputStream(outputDocument);
+
+ // load document
+ PDDocument doc = PDDocument.load(document);
+
+ // create signature dictionary
+ PDSignature signature = new PDSignature();
+ signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); // default filter
+ // subfilter for basic and PAdES Part 2 signatures
+ signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
+ signature.setName("signer name");
+ signature.setLocation("signer location");
+ signature.setReason("reason for signature");
+
+ // the signing date, needed for valid signature
+ signature.setSignDate(Calendar.getInstance());
+
+ // register signature dictionary and sign interface
+
+ if (signatureProperties != null && signatureProperties.isVisualSignEnabled()) {
+ options = new SignatureOptions();
+ options.setVisualSignature(signatureProperties);
+ // options.setPage(signatureProperties.getPage());
+ // options.setPreferedSignatureSize(signatureProperties.getPreferredSize());
+ doc.addSignature(signature, this, options);
+ } else {
+ doc.addSignature(signature, this);
+ }
+
+ // write incremental (only for signing purpose)
+ doc.saveIncremental(fis, fos);
+
+ return outputDocument;
+ }
+
+ /**
+ * <p>
+ * SignatureInterface implementation.
+ * </p>
+ * <p>
+ * This method will be called from inside of the pdfbox and create the pkcs7 signature. The given InputStream contains
+ * the bytes that are providen by the byte range.
+ * </p>
+ * <p>
+ * This method is for internal use only.
+ * </p>
+ * <p>
+ * Here the user should use his favorite cryptographic library and implement a pkcs7 signature creation.
+ * </p>
+ */
+ @Override
+ public byte[] sign(InputStream content) throws SignatureException, IOException
+ {
+ CMSProcessableInputStream input = new CMSProcessableInputStream(content);
+ CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
+ // CertificateChain
+ List<Certificate> certList = Arrays.asList(cert);
+
+ CertStore certStore = null;
+ try {
+ certStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList), provider);
+ gen.addSigner(privKey, (X509Certificate) certList.get(0), CMSSignedGenerator.DIGEST_SHA256);
+ gen.addCertificatesAndCRLs(certStore);
+ CMSSignedData signedData = gen.generate(input, false, provider);
+ return signedData.getEncoded();
+ } catch (Exception e) {
+ // should be handled
+ System.err.println("Error while creating pkcs7 signature.");
+ e.printStackTrace();
+ }
+ throw new RuntimeException("Problem while preparing signature");
+ }
+
+ /**
+ * Arguments are
+ * [0] key store
+ * [1] pin
+ * [2] document that will be signed
+ * [3] image of visible signature
+ */
+ public static void main(String[] args) throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
+ FileNotFoundException, IOException, COSVisitorException, SignatureException
+ {
+
+ if (args.length != 4) {
+ usage();
+ System.exit(1);
+ } else {
+ File ksFile = new File(args[0]);
+ KeyStore keystore = KeyStore.getInstance("PKCS12", provider);
+ char[] pin = args[1].toCharArray();
+ keystore.load(new FileInputStream(ksFile), pin);
+
+ File document = new File(args[2]);
+
+ CreateVisibleSignature signing = new CreateVisibleSignature(keystore, pin.clone());
+
+ FileInputStream image = new FileInputStream(args[3]);
+
+ PDVisibleSignDesigner visibleSig = new PDVisibleSignDesigner(args[2], image, 1);
+ visibleSig.xAxis(0).yAxis(0).zoom(-50).signatureFieldName("signature");
+
+ PDVisibleSigProperties signatureProperties = new PDVisibleSigProperties();
+
+ signatureProperties.signerName("name").signerLocation("location").signatureReason("Security").preferredSize(0)
+ .page(1).visualSignEnabled(true).setPdVisibleSignature(visibleSig).buildSignature();
+
+ signing.signPDF(document, signatureProperties);
+ }
+
+ }
+
+ /**
+ * This will print the usage for this program.
+ */
+ private static void usage()
+ {
+ System.err.println("Usage: java " + CreateSignature.class.getName()
+ + " <pkcs12-keystore-file> <pin> <input-pdf> <sign-image>");
+ }
+}
diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureOptions.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureOptions.java
index 918ad53..c305c42 100644
--- a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureOptions.java
+++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/SignatureOptions.java
@@ -21,6 +21,7 @@
import org.apache.pdfbox.cos.COSDocument;
import org.apache.pdfbox.pdfparser.VisualSignatureParser;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.PDVisibleSigProperties;
public class SignatureOptions
@@ -65,6 +66,20 @@
visParser.parse();
visualSignature = visParser.getDocument();
}
+
+ /**
+ * Reads the visual signature from the given visual signature properties
+ *
+ * @param is the <code>PDVisibleSigProperties</code> object containing the visual signature
+ *
+ * @throws IOException when something went wrong during parsing
+ *
+ * @since 1.8.3
+ */
+ public void setVisualSignature(PDVisibleSigProperties visSignatureProperties) throws IOException
+ {
+ setVisualSignature(visSignatureProperties.getVisibleSignature());
+ }
/**
* Get the visual signature.
diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDFTemplateBuilder.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDFTemplateBuilder.java
new file mode 100644
index 0000000..b4cfd7f
--- /dev/null
+++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDFTemplateBuilder.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible;
+
+import java.awt.geom.AffineTransform;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDJpeg;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
+
+/**
+ * That class builds visible signature template
+ * which will be added in our pdf document
+ * @author Vakhtang koroghlishvili (Gogebashvili)
+ *
+ */
+public interface PDFTemplateBuilder {
+
+ /**
+ * In order to create Affine Transform, using parameters
+ * @param params
+ */
+ public void createAffineTransform(byte [] params);
+
+ /**
+ * Creates specified size page
+ * @param properties
+ */
+ public void createPage(PDVisibleSignDesigner properties);
+
+ /**
+ * Creates template using page
+ * @param page
+ * @throws IOException
+ */
+ public void createTemplate(PDPage page) throws IOException;
+
+ /**
+ * Creates Acro forms in the template
+ * @param template
+ */
+ public void createAcroForm(PDDocument template);
+
+ /**
+ * Creates signature fields
+ * @param acroForm
+ * @throws IOException
+ */
+ public void createSignatureField(PDAcroForm acroForm) throws IOException;
+
+ /**
+ * Creates PDSignature
+ * @param pdSignatureField
+ * @param page
+ * @param signatureName
+ * @throws IOException
+ */
+ public void createSignature(PDSignatureField pdSignatureField, PDPage page, String signatureName) throws IOException;
+
+ /**
+ * Create AcroForm Dictionary
+ * @param acroForm
+ * @param signatureField
+ * @throws IOException
+ */
+ public void createAcroFormDictionary(PDAcroForm acroForm, PDSignatureField signatureField) throws IOException;
+
+ /**
+ * Creates SingatureRectangle
+ * @param signatureField
+ * @param properties
+ * @throws IOException
+ */
+ public void createSignatureRectangle(PDSignatureField signatureField, PDVisibleSignDesigner properties) throws IOException;
+
+ /**
+ * Creates procSetArray of PDF,Text,ImageB,ImageC,ImageI
+ */
+ public void createProcSetArray();
+
+ /**
+ * Creates signature image
+ * @param template
+ * @param InputStream
+ * @throws IOException
+ */
+ public void createSignatureImage(PDDocument template, InputStream InputStream) throws IOException;
+
+ /**
+ *
+ * @param params
+ */
+ public void createFormaterRectangle(byte [] params);
+
+ /**
+ *
+ * @param template
+ */
+ public void createHolderFormStream(PDDocument template);
+
+ /**
+ * Creates resources of form
+ */
+ public void createHolderFormResources();
+
+ /**
+ * Creates Form
+ * @param holderFormResources
+ * @param holderFormStream
+ * @param formrect
+ */
+ public void createHolderForm(PDResources holderFormResources, PDStream holderFormStream, PDRectangle formrect);
+
+ /**
+ * Creates appearance dictionary
+ * @param holderForml
+ * @param signatureField
+ * @throws IOException
+ */
+ public void createAppearanceDictionary(PDXObjectForm holderForml, PDSignatureField signatureField) throws IOException;
+
+ /**
+ *
+ * @param template
+ */
+ public void createInnerFormStream(PDDocument template);
+
+
+ /**
+ * Creates InnerForm
+ */
+ public void createInnerFormResource();
+
+ /**
+ *
+ * @param innerFormResources
+ * @param innerFormStream
+ * @param formrect
+ */
+ public void createInnerForm(PDResources innerFormResources, PDStream innerFormStream, PDRectangle formrect);
+
+
+ /**
+ *
+ * @param innerForm
+ * @param holderFormResources
+ */
+ public void insertInnerFormToHolerResources(PDXObjectForm innerForm, PDResources holderFormResources);
+
+ /**
+ *
+ * @param template
+ */
+ public void createImageFormStream(PDDocument template);
+
+ /**
+ * Create resource of image form
+ */
+ public void createImageFormResources();
+
+ /**
+ * Creates Image form
+ * @param imageFormResources
+ * @param innerFormResource
+ * @param imageFormStream
+ * @param formrect
+ * @param affineTransform
+ * @param img
+ * @throws IOException
+ */
+ public void createImageForm(PDResources imageFormResources, PDResources innerFormResource, PDStream imageFormStream, PDRectangle formrect,
+ AffineTransform affineTransform, PDJpeg img) throws IOException;
+
+ /**
+ * Inject procSetArray
+ * @param innerForm
+ * @param page
+ * @param innerFormResources
+ * @param imageFormResources
+ * @param holderFormResources
+ * @param procSet
+ */
+ public void injectProcSetArray(PDXObjectForm innerForm, PDPage page, PDResources innerFormResources, PDResources imageFormResources,
+ PDResources holderFormResources, COSArray procSet);
+
+ /**
+ * injects appearance streams
+ * @param holderFormStream
+ * @param innterFormStream
+ * @param imageFormStream
+ * @param imageObjectName
+ * @param imageName
+ * @param innerFormName
+ * @param properties
+ * @throws IOException
+ */
+ public void injectAppearanceStreams(PDStream holderFormStream, PDStream innterFormStream, PDStream imageFormStream, String imageObjectName,
+ String imageName, String innerFormName, PDVisibleSignDesigner properties) throws IOException;
+
+ /**
+ * just to create visible signature
+ * @param template
+ */
+ public void createVisualSignature(PDDocument template);
+
+ /**
+ * adds Widget Dictionary
+ * @param signatureField
+ * @param holderFormResources
+ * @throws IOException
+ */
+ public void createWidgetDictionary(PDSignatureField signatureField, PDResources holderFormResources) throws IOException;
+
+ /**
+ *
+ * @return - PDF template Structure
+ */
+ public PDFTemplateStructure getStructure();
+
+ /**
+ * Closes template
+ * @param template
+ * @throws IOException
+ */
+ public void closeTemplate(PDDocument template) throws IOException;
+}
diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDFTemplateCreator.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDFTemplateCreator.java
new file mode 100644
index 0000000..5641051
--- /dev/null
+++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDFTemplateCreator.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible;
+
+import java.awt.geom.AffineTransform;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.pdfbox.cos.COSDocument;
+import org.apache.pdfbox.exceptions.COSVisitorException;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
+
+/**
+ *
+ * @author Vakhtang koroghlishvili (Gogebashvili)
+ *
+ */
+public class PDFTemplateCreator
+{
+
+ PDFTemplateBuilder pdfBuilder;
+ private static final Log logger = LogFactory.getLog(PDFTemplateCreator.class);
+
+ /**
+ * sets PDFBuilder
+ *
+ * @param bookBuilder
+ */
+ public PDFTemplateCreator(PDFTemplateBuilder bookBuilder)
+ {
+ this.pdfBuilder = bookBuilder;
+ }
+
+ /**
+ * that method returns object of PDFStructur
+ *
+ * @return PDFStructure
+ */
+ public PDFTemplateStructure getPdfStructure()
+ {
+ return this.pdfBuilder.getStructure();
+ }
+
+ /**
+ * this method build pdf, step by step, and finally it returns stream of visible signature
+ *
+ * @param properties
+ * @return InputStream
+ * @throws IOException
+ * @throws COSVisitorException
+ */
+
+ public InputStream buildPDF(PDVisibleSignDesigner properties) throws IOException
+ {
+ logger.info("pdf building has been started");
+ PDFTemplateStructure pdfStructure = pdfBuilder.getStructure();
+
+ this.pdfBuilder.createProcSetArray();
+ this.pdfBuilder.createPage(properties);
+ PDPage page = pdfStructure.getPage();
+
+ this.pdfBuilder.createTemplate(page);
+ PDDocument template = pdfStructure.getTemplate();
+ this.pdfBuilder.createAcroForm(template);
+ PDAcroForm acroForm = pdfStructure.getAcroForm();
+
+ this.pdfBuilder.createSignatureField(acroForm);
+ PDSignatureField pdSignatureField = pdfStructure.getSignatureField();
+ this.pdfBuilder.createSignature(pdSignatureField, page, properties.getSignatureFieldName());
+ this.pdfBuilder.createAcroFormDictionary(acroForm, pdSignatureField);
+ this.pdfBuilder.createAffineTransform(properties.getAffineTransformParams());
+ AffineTransform transform = pdfStructure.getAffineTransform();
+ this.pdfBuilder.createSignatureRectangle(pdSignatureField, properties);
+ this.pdfBuilder.createFormaterRectangle(properties.getFormaterRectangleParams());
+ PDRectangle formater = pdfStructure.getFormaterRectangle();
+
+ this.pdfBuilder.createSignatureImage(template, properties.getImageStream());
+
+ this.pdfBuilder.createHolderFormStream(template);
+ PDStream holderFormStream = pdfStructure.getHolderFormStream();
+ this.pdfBuilder.createHolderFormResources();
+ PDResources holderFormResources = pdfStructure.getHolderFormResources();
+ this.pdfBuilder.createHolderForm(holderFormResources, holderFormStream, formater);
+ this.pdfBuilder.createAppearanceDictionary(pdfStructure.getHolderForm(), pdSignatureField);
+ this.pdfBuilder.createInnerFormStream(template);
+ this.pdfBuilder.createInnerFormResource();
+ PDResources innerFormResource = pdfStructure.getInnerFormResources();
+ this.pdfBuilder.createInnerForm(innerFormResource, pdfStructure.getInnterFormStream(), formater);
+ PDXObjectForm innerForm = pdfStructure.getInnerForm();
+ this.pdfBuilder.insertInnerFormToHolerResources(innerForm, holderFormResources);
+ this.pdfBuilder.createImageFormStream(template);
+ PDStream imageFormStream = pdfStructure.getImageFormStream();
+ this.pdfBuilder.createImageFormResources();
+ PDResources imageFormResources = pdfStructure.getImageFormResources();
+ this.pdfBuilder.createImageForm(imageFormResources, innerFormResource, imageFormStream, formater, transform,
+ pdfStructure.getJpedImage());
+ this.pdfBuilder.injectProcSetArray(innerForm, page, innerFormResource, imageFormResources, holderFormResources,
+ pdfStructure.getProcSet());
+
+ String imgFormName = pdfStructure.getImageFormName();
+ String imgName = pdfStructure.getImageName();
+ String innerFormName = pdfStructure.getInnerFormName();
+
+ this.pdfBuilder.injectAppearanceStreams(holderFormStream, imageFormStream, imageFormStream, imgFormName,
+ imgName, innerFormName, properties);
+ this.pdfBuilder.createVisualSignature(template);
+ this.pdfBuilder.createWidgetDictionary(pdSignatureField, holderFormResources);
+
+ ByteArrayInputStream in = null;
+ try
+ {
+ in = pdfStructure.templateAppearanceStream();
+ }
+ catch (COSVisitorException e)
+ {
+ logger.error("COSVisitorException: cant get apereance stream ", e);
+ }
+ logger.info("stream returning started, size= " + in.available());
+
+ template.close();
+
+ return in;
+
+ }
+}
diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDFTemplateStructure.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDFTemplateStructure.java
new file mode 100644
index 0000000..f8fa1da
--- /dev/null
+++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDFTemplateStructure.java
@@ -0,0 +1,365 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible;
+
+import java.awt.geom.AffineTransform;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSDocument;
+import org.apache.pdfbox.exceptions.COSVisitorException;
+import org.apache.pdfbox.pdfwriter.COSWriter;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDJpeg;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectForm;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDField;
+import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
+
+/**
+ * Structure of PDF document with visible signature
+ *
+ * @author Vakhtang koroghlishvili (Gogebashvili)
+ *
+ */
+public class PDFTemplateStructure
+{
+
+ private PDPage page;
+ private PDDocument template;
+ private PDAcroForm acroForm;
+ private PDSignatureField signatureField;
+ private PDSignature pdSignature;
+ private COSDictionary acroFormDictionary;
+ private PDRectangle singatureRectangle;
+ private AffineTransform affineTransform;
+ private COSArray procSet;
+ private PDJpeg jpedImage;
+ private PDRectangle formaterRectangle;
+ private PDStream holderFormStream;
+ private PDResources holderFormResources;
+ private PDXObjectForm holderForm;
+ private PDAppearanceDictionary appearanceDictionary;
+ private PDStream innterFormStream;
+ private PDResources innerFormResources;
+ private PDXObjectForm innerForm;
+ private PDStream imageFormStream;
+ private PDResources imageFormResources;
+ private List<PDField> acroFormFields;
+ private String innerFormName;
+ private String imageFormName;
+ private String imageName;
+ private COSDocument visualSignature;
+ private PDXObjectForm imageForm;
+ private COSDictionary widgetDictionary;
+
+ public PDPage getPage()
+ {
+ return page;
+ }
+
+ public void setPage(PDPage page)
+ {
+ this.page = page;
+ }
+
+ public PDDocument getTemplate()
+ {
+ return template;
+ }
+
+ public void setTemplate(PDDocument template)
+ {
+ this.template = template;
+ }
+
+ public PDAcroForm getAcroForm()
+ {
+ return acroForm;
+ }
+
+ public void setAcroForm(PDAcroForm acroForm)
+ {
+ this.acroForm = acroForm;
+ }
+
+ public PDSignatureField getSignatureField()
+ {
+ return signatureField;
+ }
+
+ public void setSignatureField(PDSignatureField signatureField)
+ {
+ this.signatureField = signatureField;
+ }
+
+ public PDSignature getPdSignature()
+ {
+ return pdSignature;
+ }
+
+ public void setPdSignature(PDSignature pdSignature)
+ {
+ this.pdSignature = pdSignature;
+ }
+
+ public COSDictionary getAcroFormDictionary()
+ {
+ return acroFormDictionary;
+ }
+
+ public void setAcroFormDictionary(COSDictionary acroFormDictionary)
+ {
+ this.acroFormDictionary = acroFormDictionary;
+ }
+
+ public PDRectangle getSingatureRectangle()
+ {
+ return singatureRectangle;
+ }
+
+ public void setSignatureRectangle(PDRectangle singatureRectangle)
+ {
+ this.singatureRectangle = singatureRectangle;
+ }
+
+ public AffineTransform getAffineTransform()
+ {
+ return affineTransform;
+ }
+
+ public void setAffineTransform(AffineTransform affineTransform)
+ {
+ this.affineTransform = affineTransform;
+ }
+
+ public COSArray getProcSet()
+ {
+ return procSet;
+ }
+
+ public void setProcSet(COSArray procSet)
+ {
+ this.procSet = procSet;
+ }
+
+ public PDJpeg getJpedImage()
+ {
+ return jpedImage;
+ }
+
+ public void setJpedImage(PDJpeg jpedImage)
+ {
+ this.jpedImage = jpedImage;
+ }
+
+ public PDRectangle getFormaterRectangle()
+ {
+ return formaterRectangle;
+ }
+
+ public void setFormaterRectangle(PDRectangle formaterRectangle)
+ {
+ this.formaterRectangle = formaterRectangle;
+ }
+
+ public PDStream getHolderFormStream()
+ {
+ return holderFormStream;
+ }
+
+ public void setHolderFormStream(PDStream holderFormStream)
+ {
+ this.holderFormStream = holderFormStream;
+ }
+
+ public PDXObjectForm getHolderForm()
+ {
+ return holderForm;
+ }
+
+ public void setHolderForm(PDXObjectForm holderForm)
+ {
+ this.holderForm = holderForm;
+ }
+
+ public PDResources getHolderFormResources()
+ {
+ return holderFormResources;
+ }
+
+ public void setHolderFormResources(PDResources holderFormResources)
+ {
+ this.holderFormResources = holderFormResources;
+ }
+
+ public PDAppearanceDictionary getAppearanceDictionary()
+ {
+ return appearanceDictionary;
+ }
+
+ public void setAppearanceDictionary(PDAppearanceDictionary appearanceDictionary)
+ {
+ this.appearanceDictionary = appearanceDictionary;
+ }
+
+ public PDStream getInnterFormStream()
+ {
+ return innterFormStream;
+ }
+
+ public void setInnterFormStream(PDStream innterFormStream)
+ {
+ this.innterFormStream = innterFormStream;
+ }
+
+ public PDResources getInnerFormResources()
+ {
+ return innerFormResources;
+ }
+
+ public void setInnerFormResources(PDResources innerFormResources)
+ {
+ this.innerFormResources = innerFormResources;
+ }
+
+ public PDXObjectForm getInnerForm()
+ {
+ return innerForm;
+ }
+
+ public void setInnerForm(PDXObjectForm innerForm)
+ {
+ this.innerForm = innerForm;
+ }
+
+ public String getInnerFormName()
+ {
+ return innerFormName;
+ }
+
+ public void setInnerFormName(String innerFormName)
+ {
+ this.innerFormName = innerFormName;
+ }
+
+ public PDStream getImageFormStream()
+ {
+ return imageFormStream;
+ }
+
+ public void setImageFormStream(PDStream imageFormStream)
+ {
+ this.imageFormStream = imageFormStream;
+ }
+
+ public PDResources getImageFormResources()
+ {
+ return imageFormResources;
+ }
+
+ public void setImageFormResources(PDResources imageFormResources)
+ {
+ this.imageFormResources = imageFormResources;
+ }
+
+ public PDXObjectForm getImageForm()
+ {
+ return imageForm;
+ }
+
+ public void setImageForm(PDXObjectForm imageForm)
+ {
+ this.imageForm = imageForm;
+ }
+
+ public String getImageFormName()
+ {
+ return imageFormName;
+ }
+
+ public void setImageFormName(String imageFormName)
+ {
+ this.imageFormName = imageFormName;
+ }
+
+ public String getImageName()
+ {
+ return imageName;
+ }
+
+ public void setImageName(String imageName)
+ {
+ this.imageName = imageName;
+ }
+
+ public COSDocument getVisualSignature()
+ {
+ return visualSignature;
+ }
+
+ public void setVisualSignature(COSDocument visualSignature)
+ {
+ this.visualSignature = visualSignature;
+ }
+
+ public List<PDField> getAcroFormFields()
+ {
+ return acroFormFields;
+ }
+
+ public void setAcroFormFields(List<PDField> acroFormFields)
+ {
+ this.acroFormFields = acroFormFields;
+ }
+
+ public ByteArrayInputStream templateAppearanceStream() throws IOException, COSVisitorException
+ {
+ COSDocument visualSignature = getVisualSignature();
+
+ ByteArrayOutputStream memoryOut = new ByteArrayOutputStream();
+ COSWriter memoryWriter = new COSWriter(memoryOut);
+ memoryWriter.write(visualSignature);
+
+ ByteArrayInputStream input = new ByteArrayInputStream(memoryOut.toByteArray());
+
+ getTemplate().close();
+
+ return input;
+ }
+
+ public COSDictionary getWidgetDictionary()
+ {
+ return widgetDictionary;
+ }
+
+ public void setWidgetDictionary(COSDictionary widgetDictionary)
+ {
+ this.widgetDictionary = widgetDictionary;
+ }
+
+}
diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDVisibleSigBuilder.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDVisibleSigBuilder.java
new file mode 100644
index 0000000..f28637c
--- /dev/null
+++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDVisibleSigBuilder.java
@@ -0,0 +1,398 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible;
+
+import java.awt.geom.AffineTransform;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.common.PDStream;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDJpeg;
+import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectForm;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
+import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
+import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
+import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
+import org.apache.pdfbox.pdmodel.interactive.form.PDField;
+import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
+
+/**
+ *
+ * @author Vakhtang koroghlishvili (Gogebashvili)
+ *
+ */
+public class PDVisibleSigBuilder implements PDFTemplateBuilder
+{
+
+ private PDFTemplateStructure pdfStructure;
+ private static final Log logger = LogFactory.getLog(PDVisibleSigBuilder.class);
+
+ @Override
+ public void createPage(PDVisibleSignDesigner properties)
+ {
+ PDPage page = new PDPage();
+ page.setMediaBox(new PDRectangle(properties.getPageWidth(), properties.getPageHeight()));
+ pdfStructure.setPage(page);
+ logger.info("PDF page has been created");
+ }
+
+ @Override
+ public void createTemplate(PDPage page) throws IOException
+ {
+ PDDocument template = new PDDocument();
+ template.addPage(page);
+ pdfStructure.setTemplate(template);
+ }
+
+ public PDVisibleSigBuilder()
+ {
+ pdfStructure = new PDFTemplateStructure();
+ logger.info("PDF Strucure has been Created");
+
+ }
+
+ @Override
+ public void createAcroForm(PDDocument template)
+ {
+ PDAcroForm theAcroForm = new PDAcroForm(template);
+ template.getDocumentCatalog().setAcroForm(theAcroForm);
+ pdfStructure.setAcroForm(theAcroForm);
+ logger.info("Acro form page has been created");
+
+ }
+
+ @Override
+ public PDFTemplateStructure getStructure()
+ {
+ return pdfStructure;
+ }
+
+ @Override
+ public void createSignatureField(PDAcroForm acroForm) throws IOException
+ {
+ PDSignatureField sf = new PDSignatureField(acroForm);
+ pdfStructure.setSignatureField(sf);
+ logger.info("Signature field has been created");
+ }
+
+ @Override
+ public void createSignature(PDSignatureField pdSignatureField, PDPage page, String signatureName)
+ throws IOException
+ {
+ PDSignature pdSignature = new PDSignature();
+ pdSignatureField.setSignature(pdSignature);
+ pdSignatureField.getWidget().setPage(page);
+ page.getAnnotations().add(pdSignatureField.getWidget());
+ pdSignature.setName(signatureName);
+ pdSignature.setByteRange(new int[] { 0, 0, 0, 0 });
+ pdSignature.setContents(new byte[4096]);
+ pdfStructure.setPdSignature(pdSignature);
+ logger.info("PDSignatur has been created");
+ }
+
+ @Override
+ public void createAcroFormDictionary(PDAcroForm acroForm, PDSignatureField signatureField) throws IOException
+ {
+ @SuppressWarnings("unchecked")
+ List<PDField> acroFormFields = acroForm.getFields();
+ COSDictionary acroFormDict = acroForm.getDictionary();
+ acroFormDict.setDirect(true);
+ acroFormDict.setInt(COSName.SIG_FLAGS, 3);
+ acroFormFields.add(signatureField);
+ acroFormDict.setString(COSName.DA, "/sylfaen 0 Tf 0 g");
+ pdfStructure.setAcroFormFields(acroFormFields);
+ pdfStructure.setAcroFormDictionary(acroFormDict);
+ logger.info("AcroForm dictionary has been created");
+ }
+
+ @Override
+ public void createSignatureRectangle(PDSignatureField signatureField, PDVisibleSignDesigner properties)
+ throws IOException
+ {
+
+ PDRectangle rect = new PDRectangle();
+ rect.setUpperRightX(properties.getxAxis() + properties.getWidth());
+ rect.setUpperRightY(properties.getTemplateHeight() - properties.getyAxis());
+ rect.setLowerLeftY(properties.getTemplateHeight() - properties.getyAxis() - properties.getHeight());
+ rect.setLowerLeftX(properties.getxAxis());
+ signatureField.getWidget().setRectangle(rect);
+ pdfStructure.setSignatureRectangle(rect);
+ logger.info("rectangle of signature has been created");
+ }
+
+ @Override
+ public void createAffineTransform(byte[] params)
+ {
+ AffineTransform transform = new AffineTransform(params[0], params[1], params[2], params[3], params[4],
+ params[5]);
+ pdfStructure.setAffineTransform(transform);
+ logger.info("Matrix has been added");
+ }
+
+ @Override
+ public void createProcSetArray()
+ {
+ COSArray procSetArr = new COSArray();
+ procSetArr.add(COSName.getPDFName("PDF"));
+ procSetArr.add(COSName.getPDFName("Text"));
+ procSetArr.add(COSName.getPDFName("ImageB"));
+ procSetArr.add(COSName.getPDFName("ImageC"));
+ procSetArr.add(COSName.getPDFName("ImageI"));
+ pdfStructure.setProcSet(procSetArr);
+ logger.info("ProcSet array has been created");
+ }
+
+ @Override
+ public void createSignatureImage(PDDocument template, InputStream inputStream) throws IOException
+ {
+ PDJpeg img = new PDJpeg(template, inputStream);
+ pdfStructure.setJpedImage(img);
+ logger.info("Visible Signature Image has been created");
+ // pdfStructure.setTemplate(template);
+ inputStream.close();
+
+ }
+
+ @Override
+ public void createFormaterRectangle(byte[] params)
+ {
+
+ PDRectangle formrect = new PDRectangle();
+ formrect.setUpperRightX(params[0]);
+ formrect.setUpperRightY(params[1]);
+ formrect.setLowerLeftX(params[2]);
+ formrect.setLowerLeftY(params[3]);
+
+ pdfStructure.setFormaterRectangle(formrect);
+ logger.info("Formater rectangle has been created");
+
+ }
+
+ @Override
+ public void createHolderFormStream(PDDocument template)
+ {
+ PDStream holderForm = new PDStream(template);
+ pdfStructure.setHolderFormStream(holderForm);
+ logger.info("Holder form Stream has been created");
+ }
+
+ @Override
+ public void createHolderFormResources()
+ {
+ PDResources holderFormResources = new PDResources();
+ pdfStructure.setHolderFormResources(holderFormResources);
+ logger.info("Holder form resources have been created");
+
+ }
+
+ @Override
+ public void createHolderForm(PDResources holderFormResources, PDStream holderFormStream, PDRectangle formrect)
+ {
+
+ PDXObjectForm holderForm = new PDXObjectForm(holderFormStream);
+ holderForm.setResources(holderFormResources);
+ holderForm.setBBox(formrect);
+ holderForm.setFormType(1);
+ pdfStructure.setHolderForm(holderForm);
+ logger.info("Holder form has been created");
+
+ }
+
+ @Override
+ public void createAppearanceDictionary(PDXObjectForm holderForml, PDSignatureField signatureField)
+ throws IOException
+ {
+
+ PDAppearanceDictionary appearance = new PDAppearanceDictionary();
+ appearance.getCOSObject().setDirect(true);
+
+ PDAppearanceStream appearanceStream = new PDAppearanceStream(holderForml.getCOSStream());
+
+ appearance.setNormalAppearance(appearanceStream);
+ signatureField.getWidget().setAppearance(appearance);
+
+ pdfStructure.setAppearanceDictionary(appearance);
+ logger.info("PDF appereance Dictionary has been created");
+
+ }
+
+ @Override
+ public void createInnerFormStream(PDDocument template)
+ {
+ PDStream innterFormStream = new PDStream(template);
+ pdfStructure.setInnterFormStream(innterFormStream);
+ logger.info("Strean of another form (inner form - it would be inside holder form) has been created");
+ }
+
+ @Override
+ public void createInnerFormResource()
+ {
+ PDResources innerFormResources = new PDResources();
+ pdfStructure.setInnerFormResources(innerFormResources);
+ logger.info("Resources of another form (inner form - it would be inside holder form) have been created");
+ }
+
+ @Override
+ public void createInnerForm(PDResources innerFormResources, PDStream innerFormStream, PDRectangle formrect)
+ {
+ PDXObjectForm innerForm = new PDXObjectForm(innerFormStream);
+ innerForm.setResources(innerFormResources);
+ innerForm.setBBox(formrect);
+ innerForm.setFormType(1);
+ pdfStructure.setInnerForm(innerForm);
+ logger.info("Another form (inner form - it would be inside holder form) have been created");
+
+ }
+
+ @Override
+ public void insertInnerFormToHolerResources(PDXObjectForm innerForm, PDResources holderFormResources)
+ {
+ String name = holderFormResources.addXObject(innerForm, "FRM");
+ pdfStructure.setInnerFormName(name);
+ logger.info("Alerady inserted inner form inside holder form");
+ }
+
+ @Override
+ public void createImageFormStream(PDDocument template)
+ {
+ PDStream imageFormStream = new PDStream(template);
+ pdfStructure.setImageFormStream(imageFormStream);
+ logger.info("Created image form Stream");
+
+ }
+
+ @Override
+ public void createImageFormResources()
+ {
+ PDResources imageFormResources = new PDResources();
+ pdfStructure.setImageFormResources(imageFormResources);
+ logger.info("Created image form Resources");
+ }
+
+ @Override
+ public void createImageForm(PDResources imageFormResources, PDResources innerFormResource,
+ PDStream imageFormStream, PDRectangle formrect, AffineTransform affineTransform, PDJpeg img)
+ throws IOException
+ {
+
+ /*
+ * if you need text on the visible signature
+ *
+ * PDFont font = PDTrueTypeFont.loadTTF(this.pdfStructure.getTemplate(), new File("D:\\arial.ttf"));
+ * font.setFontEncoding(new WinAnsiEncoding());
+ *
+ * Map<String, PDFont> fonts = new HashMap<String, PDFont>(); fonts.put("arial", font);
+ */
+ PDXObjectForm imageForm = new PDXObjectForm(imageFormStream);
+ imageForm.setBBox(formrect);
+ imageForm.setMatrix(affineTransform);
+ imageForm.setResources(imageFormResources);
+ imageForm.setFormType(1);
+ /*
+ * imageForm.getResources().addFont(font);
+ * imageForm.getResources().setFonts(fonts);
+ */
+
+ imageFormResources.getCOSObject().setDirect(true);
+ String imageFormName = innerFormResource.addXObject(imageForm, "n");
+ String imageName = imageFormResources.addXObject(img, "img");
+ this.pdfStructure.setImageForm(imageForm);
+ this.pdfStructure.setImageFormName(imageFormName);
+ this.pdfStructure.setImageName(imageName);
+ logger.info("Created image form");
+ }
+
+ @Override
+ public void injectProcSetArray(PDXObjectForm innerForm, PDPage page, PDResources innerFormResources,
+ PDResources imageFormResources, PDResources holderFormResources, COSArray procSet)
+ {
+
+ innerForm.getResources().getCOSDictionary().setItem(COSName.PROC_SET, procSet); //
+ page.getCOSDictionary().setItem(COSName.PROC_SET, procSet);
+ innerFormResources.getCOSDictionary().setItem(COSName.PROC_SET, procSet);
+ imageFormResources.getCOSDictionary().setItem(COSName.PROC_SET, procSet);
+ holderFormResources.getCOSDictionary().setItem(COSName.PROC_SET, procSet);
+ logger.info("inserted ProcSet to PDF");
+ }
+
+ @Override
+ public void injectAppearanceStreams(PDStream holderFormStream, PDStream innterFormStream, PDStream imageFormStream,
+ String imageObjectName, String imageName, String innerFormName, PDVisibleSignDesigner properties)
+ throws IOException
+ {
+
+ // 100 means that document width is 100% via the rectangle. if rectangle
+ // is 500px, images 100% is 500px.
+ // String imgFormComment = "q "+imageWidthSize+ " 0 0 50 0 0 cm /" +
+ // imageName + " Do Q\n" + builder.toString();
+ String imgFormComment = "q " + 100 + " 0 0 50 0 0 cm /" + imageName + " Do Q\n";
+ String holderFormComment = "q 1 0 0 1 0 0 cm /" + innerFormName + " Do Q \n";
+ String innerFormComment = "q 1 0 0 1 0 0 cm /" + imageObjectName + " Do Q\n";
+
+ appendRawCommands(pdfStructure.getHolderFormStream().createOutputStream(), holderFormComment);
+ appendRawCommands(pdfStructure.getInnterFormStream().createOutputStream(), innerFormComment);
+ appendRawCommands(pdfStructure.getImageFormStream().createOutputStream(), imgFormComment);
+ logger.info("Injected apereance stream to pdf");
+
+ }
+
+ public void appendRawCommands(OutputStream os, String commands) throws IOException
+ {
+ os.write(commands.getBytes("UTF-8"));
+ os.close();
+ }
+
+ @Override
+ public void createVisualSignature(PDDocument template)
+ {
+ this.pdfStructure.setVisualSignature(template.getDocument());
+ logger.info("Visible signature has been created");
+
+ }
+
+ @Override
+ public void createWidgetDictionary(PDSignatureField signatureField, PDResources holderFormResources)
+ throws IOException
+ {
+
+ COSDictionary widgetDict = signatureField.getWidget().getDictionary();
+ widgetDict.setNeedToBeUpdate(true);
+ widgetDict.setItem(COSName.DR, holderFormResources.getCOSObject());
+
+ pdfStructure.setWidgetDictionary(widgetDict);
+ logger.info("WidgetDictionary has been crated");
+ }
+
+ @Override
+ public void closeTemplate(PDDocument template) throws IOException
+ {
+ template.close();
+ this.pdfStructure.getTemplate().close();
+
+ }
+}
diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDVisibleSigProperties.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDVisibleSigProperties.java
new file mode 100644
index 0000000..6b595b0
--- /dev/null
+++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDVisibleSigProperties.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.pdfbox.cos.COSDictionary;
+import org.apache.pdfbox.cos.COSDocument;
+
+/**
+ * This builder class is in order to create visible signature properties.
+ *
+ * @author Vakhtang koroghlishvili (Gogebashvili)
+ *
+ */
+public class PDVisibleSigProperties
+{
+ private String signerName;
+ private String signerLocation;
+ private String signatureReason;
+ private boolean visualSignEnabled;
+ private int page;
+ private int preferredSize;
+
+ private InputStream visibleSignature;
+ private PDVisibleSignDesigner pdVisibleSignature;
+
+ /**
+ * start building of visible signature
+ *
+ * @throws IOException
+ */
+ public void buildSignature() throws IOException
+ {
+ PDFTemplateBuilder builder = new PDVisibleSigBuilder();
+ PDFTemplateCreator creator = new PDFTemplateCreator(builder);
+ setVisibleSignature(creator.buildPDF(getPdVisibleSignature()));
+ }
+
+ /**
+ *
+ * @return - signer name
+ */
+ public String getSignerName()
+ {
+ return signerName;
+ }
+
+ /**
+ * sets signer name
+ * @param signerName
+ * @return
+ */
+ public PDVisibleSigProperties signerName(String signerName)
+ {
+ this.signerName = signerName;
+ return this;
+ }
+
+ /**
+ *
+ * @return - location
+ */
+ public String getSignerLocation()
+ {
+ return signerLocation;
+ }
+
+ /**
+ * Sets location
+ * @param signerLocation
+ * @return
+ */
+ public PDVisibleSigProperties signerLocation(String signerLocation)
+ {
+ this.signerLocation = signerLocation;
+ return this;
+ }
+
+ /**
+ * gets reason of signing
+ * @return
+ */
+ public String getSignatureReason()
+ {
+ return signatureReason;
+ }
+
+ /**
+ * sets reason of signing
+ * @param signatureReason
+ * @return
+ */
+ public PDVisibleSigProperties signatureReason(String signatureReason)
+ {
+ this.signatureReason = signatureReason;
+ return this;
+ }
+
+ /**
+ * returns your page
+ * @return
+ */
+ public int getPage()
+ {
+ return page;
+ }
+
+ /**
+ * sets page number
+ * @param page
+ * @return
+ */
+ public PDVisibleSigProperties page(int page)
+ {
+ this.page = page;
+ return this;
+ }
+
+ /**
+ * gets our preferred size
+ * @return
+ */
+ public int getPreferredSize()
+ {
+ return preferredSize;
+ }
+
+ /**
+ * sets our preferred size
+ * @param preferredSize
+ * @return
+ */
+ public PDVisibleSigProperties preferredSize(int preferredSize)
+ {
+ this.preferredSize = preferredSize;
+ return this;
+ }
+
+ /**
+ * checks if we need to add visible signature
+ * @return
+ */
+ public boolean isVisualSignEnabled()
+ {
+ return visualSignEnabled;
+ }
+
+ /**
+ * sets visible signature to be added or not
+ * @param visualSignEnabled
+ * @return
+ */
+ public PDVisibleSigProperties visualSignEnabled(boolean visualSignEnabled)
+ {
+ this.visualSignEnabled = visualSignEnabled;
+ return this;
+ }
+
+ /**
+ * this method gets visible signature configuration object
+ * @return
+ */
+ public PDVisibleSignDesigner getPdVisibleSignature()
+ {
+ return pdVisibleSignature;
+ }
+
+ /**
+ * Sets visible signature configuration Object
+ * @param pdVisibleSignature
+ * @return
+ */
+ public PDVisibleSigProperties setPdVisibleSignature(PDVisibleSignDesigner pdVisibleSignature)
+ {
+ this.pdVisibleSignature = pdVisibleSignature;
+ return this;
+ }
+
+ /**
+ * returns visible signature configuration object
+ * @return
+ */
+ public InputStream getVisibleSignature()
+ {
+ return visibleSignature;
+ }
+
+ /**
+ * sets configuration object of visible signature
+ * @param visibleSignature
+ */
+ public void setVisibleSignature(InputStream visibleSignature)
+ {
+ this.visibleSignature = visibleSignature;
+ }
+
+}
diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDVisibleSignDesigner.java b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDVisibleSignDesigner.java
new file mode 100644
index 0000000..a69e94c
--- /dev/null
+++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/PDVisibleSignDesigner.java
@@ -0,0 +1,457 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.bouncycastle.util.Arrays;
+
+/**
+ *
+ * That class is in order to build your
+ * visible signature design. Because of
+ * this is builder, instead of setParam()
+ * we use param() methods.
+ * @author Vakhtang koroghlishvili (Gogebashvili)
+ */
+public class PDVisibleSignDesigner
+{
+
+ private Float sigImgWidth;
+ private Float sigImgHeight;
+ private float xAxis;
+ private float yAxis;
+ private float pageHeight;
+ private float pageWidth;
+ private InputStream imgageStream;
+ private String signatureFieldName = "sig"; // default
+ private byte[] formaterRectangleParams = { 0, 0, 100, 50 }; // default
+ private byte[] AffineTransformParams = { 1, 0, 0, 1, 0, 0 }; // default
+ private float imageSizeInPercents;
+ private PDDocument document = null;
+
+
+
+ /**
+ *
+ * @param originalDocumenStream
+ * @param imageStream
+ * @param page-which page are you going to add visible signature
+ * @throws IOException
+ */
+ public PDVisibleSignDesigner(InputStream originalDocumenStream, InputStream imageStream, int page) throws IOException
+ {
+ signatureImageStream(imageStream);
+ document = PDDocument.load(originalDocumenStream);
+ calculatePageSize(document, page);
+ }
+
+ /**
+ *
+ * @param documentPath
+ * @param imageStream
+ * @param page -which page are you going to add visible signature
+ * @throws IOException
+ */
+ public PDVisibleSignDesigner(String documentPath, InputStream imageStream, int page) throws IOException
+ {
+
+ // set visible singature image Input stream
+ signatureImageStream(imageStream);
+
+ // create PD document
+ document = PDDocument.load(documentPath);
+
+ // calculate height an width of document
+ calculatePageSize(document, page);
+
+ document.close();
+ }
+
+ public PDVisibleSignDesigner(PDDocument doc, InputStream imageStream, int page) throws IOException
+ {
+ signatureImageStream(imageStream);
+ calculatePageSize(doc, page);
+ }
+
+ /**
+ * Each page of document can be different sizes.
+ *
+ * @param document
+ * @param page
+ */
+ private void calculatePageSize(PDDocument document, int page)
+ {
+
+ if (page < 1)
+ {
+ throw new IllegalArgumentException("First page of pdf is 1, not " + page);
+ }
+
+ List<?> pages = document.getDocumentCatalog().getAllPages();
+ PDPage firstPage =(PDPage) pages.get(page - 1);
+ PDRectangle mediaBox = firstPage.findMediaBox();
+ this.pageHeight(mediaBox.getHeight());
+ this.pageWidth = mediaBox.getWidth();
+
+ float x = this.pageWidth;
+ float y = 0;
+ this.pageWidth = this.pageWidth + y;
+ float tPercent = (100 * y / (x + y));
+ this.imageSizeInPercents = 100 - tPercent;
+
+ }
+
+ /**
+ *
+ * @param path of image location
+ * @return image Stream
+ * @throws IOException
+ */
+ public PDVisibleSignDesigner signatureImage(String path) throws IOException
+ {
+ InputStream fin = new FileInputStream(path);
+ return signatureImageStream(fin);
+ }
+
+ /**
+ * zoom signature image with some percent.
+ *
+ * @param percent- x % increase image with x percent.
+ * @return Visible Signature Configuration Object
+ */
+ public PDVisibleSignDesigner zoom(float percent)
+ {
+ sigImgHeight = sigImgHeight + (sigImgHeight * percent) / 100;
+ sigImgWidth = sigImgWidth + (sigImgWidth * percent) / 100;
+ return this;
+ }
+
+ /**
+ *
+ * @param xAxis - x coordinate
+ * @param yAxis - y coordinate
+ * @return Visible Signature Configuration Object
+ */
+ public PDVisibleSignDesigner coordinates(float x, float y)
+ {
+ xAxis(x);
+ yAxis(y);
+ return this;
+ }
+
+ /**
+ *
+ * @return xAxis - gets x coordinates
+ */
+ public float getxAxis()
+ {
+ return xAxis;
+ }
+
+ /**
+ *
+ * @param xAxis - x coordinate
+ * @return Visible Signature Configuration Object
+ */
+ public PDVisibleSignDesigner xAxis(float xAxis)
+ {
+ this.xAxis = xAxis;
+ return this;
+ }
+
+ /**
+ *
+ * @return yAxis
+ */
+ public float getyAxis()
+ {
+ return yAxis;
+ }
+
+ /**
+ *
+ * @param yAxis
+ * @return Visible Signature Configuration Object
+ */
+ public PDVisibleSignDesigner yAxis(float yAxis)
+ {
+ this.yAxis = yAxis;
+ return this;
+ }
+
+ /**
+ *
+ * @return signature image width
+ */
+ public float getWidth()
+ {
+ return sigImgWidth;
+ }
+
+ /**
+ *
+ * @param sets signature image width
+ * @return Visible Signature Configuration Object
+ */
+ public PDVisibleSignDesigner width(float signatureImgWidth)
+ {
+ this.sigImgWidth = signatureImgWidth;
+ return this;
+ }
+
+ /**
+ *
+ * @return signature image height
+ */
+ public float getHeight()
+ {
+ return sigImgHeight;
+ }
+
+ /**
+ *
+ * @param set signature image Height
+ * @return Visible Signature Configuration Object
+ */
+ public PDVisibleSignDesigner height(float signatureImgHeight)
+ {
+ this.sigImgHeight = signatureImgHeight;
+ return this;
+ }
+
+ /**
+ *
+ * @return template height
+ */
+ protected float getTemplateHeight()
+ {
+ return getPageHeight();
+ }
+
+ /**
+ *
+ * @param templateHeight
+ * @return Visible Signature Configuration Object
+ */
+ private PDVisibleSignDesigner pageHeight(float templateHeight)
+ {
+ this.pageHeight = templateHeight;
+ return this;
+ }
+
+ /**
+ *
+ * @return signature field name
+ */
+ public String getSignatureFieldName()
+ {
+ return signatureFieldName;
+ }
+
+ /**
+ *
+ * @param signatureFieldName
+ * @return Visible Signature Configuration Object
+ */
+ public PDVisibleSignDesigner signatureFieldName(String signatureFieldName)
+ {
+ this.signatureFieldName = signatureFieldName;
+ return this;
+ }
+
+ /**
+ *
+ * @return image Stream
+ */
+ public InputStream getImageStream()
+ {
+ return imgageStream;
+ }
+
+ /**
+ *
+ * @param imgageStream- stream of your visible signature image
+ * @return Visible Signature Configuration Object
+ * @throws IOException
+ */
+ private PDVisibleSignDesigner signatureImageStream(InputStream imgageStream) throws IOException
+ {
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int len;
+ while ((len = imgageStream.read(buffer)) > -1)
+ {
+ baos.write(buffer, 0, len);
+ }
+ baos.flush();
+ baos.close();
+
+ byte[] byteArray = baos.toByteArray();
+ byte[] byteArraySecond = Arrays.clone(byteArray);
+
+ InputStream inputForBufferedImage = new ByteArrayInputStream(byteArray);
+ InputStream revertInputStream = new ByteArrayInputStream(byteArraySecond);
+
+ if (sigImgHeight == null || sigImgWidth == null)
+ {
+ calcualteImageSize(inputForBufferedImage);
+ }
+
+ this.imgageStream = revertInputStream;
+
+ return this;
+ }
+
+ /**
+ * calculates image width and height. sported formats: all
+ *
+ * @param fis - input stream of image
+ * @throws IOException
+ */
+ private void calcualteImageSize(InputStream fis) throws IOException
+ {
+
+ BufferedImage bimg = ImageIO.read(fis);
+ int width = bimg.getWidth();
+ int height = bimg.getHeight();
+
+ sigImgHeight = (float) height;
+ sigImgWidth = (float) width;
+
+ }
+
+ /**
+ *
+ * @return Affine Transform parameters of for PDF Matrix
+ */
+ public byte[] getAffineTransformParams()
+ {
+ return AffineTransformParams;
+ }
+
+ /**
+ *
+ * @param affineTransformParams
+ * @return Visible Signature Configuration Object
+ */
+ public PDVisibleSignDesigner affineTransformParams(byte[] affineTransformParams)
+ {
+ AffineTransformParams = affineTransformParams;
+ return this;
+ }
+
+ /**
+ *
+ * @return formatter PDRectanle parameters
+ */
+ public byte[] getFormaterRectangleParams()
+ {
+ return formaterRectangleParams;
+ }
+
+ /**
+ * sets formatter PDRectangle;
+ *
+ * @param formaterRectangleParams
+ * @return Visible Signature Configuration Object
+ */
+ public PDVisibleSignDesigner formaterRectangleParams(byte[] formaterRectangleParams)
+ {
+ this.formaterRectangleParams = formaterRectangleParams;
+ return this;
+ }
+
+ /**
+ *
+ * @return page width
+ */
+ public float getPageWidth()
+ {
+ return pageWidth;
+ }
+
+ /**
+ *
+ * @param sets pageWidth
+ * @return Visible Signature Configuration Object
+ */
+ public PDVisibleSignDesigner pageWidth(float pageWidth)
+ {
+ this.pageWidth = pageWidth;
+ return this;
+ }
+
+ /**
+ *
+ * @return page height
+ */
+ public float getPageHeight()
+ {
+ return pageHeight;
+ }
+
+ /**
+ * get image size in percents
+ * @return
+ */
+ public float getImageSizeInPercents()
+ {
+ return imageSizeInPercents;
+ }
+
+ /**
+ *
+ * @param imageSizeInPercents
+ */
+ public void imageSizeInPercents(float imageSizeInPercents)
+ {
+ this.imageSizeInPercents = imageSizeInPercents;
+ }
+
+ /**
+ * returns visible signature text
+ * @return
+ */
+ public String getSignatureText()
+ {
+ throw new UnsupportedOperationException("That method is not yet implemented");
+ }
+
+ /**
+ *
+ * @param signatureText - adds the text on visible signature
+ * @return
+ */
+ public PDVisibleSignDesigner signatureText(String signatureText)
+ {
+ throw new UnsupportedOperationException("That method is not yet implemented");
+ }
+
+}
diff --git a/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/package.html b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/package.html
new file mode 100644
index 0000000..cabf72c
--- /dev/null
+++ b/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/interactive/digitalsignature/visible/package.html
@@ -0,0 +1,25 @@
+<!--
+ ! Licensed to the Apache Software Foundation (ASF) under one or more
+ ! contributor license agreements. See the NOTICE file distributed with
+ ! this work for additional information regarding copyright ownership.
+ ! The ASF licenses this file to You under the Apache License, Version 2.0
+ ! (the "License"); you may not use this file except in compliance with
+ ! the License. You may obtain a copy of the License at
+ !
+ ! http://www.apache.org/licenses/LICENSE-2.0
+ !
+ ! Unless required by applicable law or agreed to in writing, software
+ ! distributed under the License is distributed on an "AS IS" BASIS,
+ ! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ! See the License for the specific language governing permissions and
+ ! limitations under the License.
+ !-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+This is the visual signature part that help creating the visual representation for the digital signature.
+</body>
+</html>