Standalone java app that takes user input of a folder location.
The app will open a pdf document, search for a signature block, report how many signature blocks and signatures it has found and output to an external txt file in the same folder.
In the output file it will list the file opened and number of signature blocks found. Once the app has finished searching the document. It will close the pdf.
It will then search for another pdf in the same folder and repeat the above items.
package com.example;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.pdfbox.Loader;
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.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField;
import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
import org.apache.pdfbox.text.PDFTextStripper;
public class PDFSignApp {
private static final Pattern[] SIGNATURE_PATTERNS = { Pattern.compile("(?i)signature\\s*:", Pattern.MULTILINE),
Pattern.compile("(?i)signed\\s*by\\s*:", Pattern.MULTILINE), Pattern.compile("(?i)electronically\\s*signed", Pattern.MULTILINE),
Pattern.compile("(?i)digital\\s*signature", Pattern.MULTILINE),
Pattern.compile("(?i)\\b(sign|signed|signature)\\s*(here|below)", Pattern.MULTILINE), Pattern.compile("(?i)x\\s*_+", Pattern.MULTILINE), // X
// followed
// by
// underscores
Pattern.compile("_+\\s*signature", Pattern.MULTILINE), Pattern.compile("(?i)date\\s*signed", Pattern.MULTILINE),
Pattern.compile("(?i)authorized\\s*signature", Pattern.MULTILINE),
Pattern.compile("(?i)deputy\\s+executive\\s+director", Pattern.MULTILINE) };
private static final String OUTPUT_FILENAME = "signature_report.txt";
private static final String SIGNATURE_REASON = "Approval";
private static final String SIGNATURE_LOCATION = "Digital Document";
private static final String DEFAULT_SIGNER_NAME = "Executive Director";
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.println("=== PDF Signature Block & Form Filler ===");
System.out.print("Enter the folder path containing PDF files: ");
String folderPath = scanner.nextLine().trim();
// Remove quotes if present
if (folderPath.startsWith("\"") && folderPath.endsWith("\"")) {
folderPath = folderPath.substring(1, folderPath.length() - 1);
}
Path folder = Paths.get(folderPath);
if (!Files.exists(folder) || !Files.isDirectory(folder)) {
System.err.println("Error: The specified path is not a valid directory.");
return;
}
System.out.print("Do you want to add signatures? (y/n): ");
boolean fillFields = scanner.nextLine().trim().toLowerCase().startsWith("y");
String customSignatureText = "";
String customDateText = "";
boolean useDigitalSignature = false;
if (fillFields) {
System.out.print("Enter the name to write in the 'Signature_Executive Director' field: ");
customSignatureText = scanner.nextLine().trim();
if (customSignatureText.isEmpty()) {
customSignatureText = "John Doe";
}
System.out.print("Enter the date in MM/dd/yyyy format for the 'Date_Executive Director' field: ");
customDateText = scanner.nextLine().trim();
// Validate date format
while (!isValidDateFormat(customDateText)) {
System.out.println("Invalid date format. Please use MM/dd/yyyy format (e.g., 03/15/2024)");
System.out.print("Enter the date in MM/dd/yyyy format: ");
customDateText = scanner.nextLine().trim();
}
System.out.print("Create digital signature (recommended) or text signature? (d/t): ");
String signatureType = scanner.nextLine().trim().toLowerCase();
useDigitalSignature = signatureType.startsWith("d");
}
try {
processPDFsInFolder(folder, fillFields, useDigitalSignature, customSignatureText, customDateText);
} catch (Exception e) {
System.err.println("Error processing PDFs: " + e.getMessage());
e.printStackTrace();
}
System.out.println("Processing completed. Press Enter to exit...");
scanner.nextLine();
} finally {
scanner.close();
}
}
/**
* Process all PDF files in the specified folder
*/
private static void processPDFsInFolder(Path folder, boolean fillFields, boolean useDigitalSignature, String customSignatureText,
String customDateText) throws IOException {
List<Path> pdfFiles = new ArrayList<>();
// Find all PDF files in the folder
try (DirectoryStream<Path> stream = Files.newDirectoryStream(folder, "*.pdf")) {
for (Path file : stream) {
if (Files.isRegularFile(file)) {
pdfFiles.add(file);
}
}
}
if (pdfFiles.isEmpty()) {
System.out.println("No PDF files found in the specified folder.");
return;
}
System.out.println("Found " + pdfFiles.size() + " PDF file(s) to process.");
Path outputFile = folder.resolve(OUTPUT_FILENAME);
List<String> reportLines = new ArrayList<>();
// Add header to report
reportLines.add("PDF Signature Block Detection & Digital Signing Report");
reportLines.add("Generated: " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
reportLines.add("Folder: " + folder.toAbsolutePath());
reportLines.add("Fill Fields: " + fillFields);
reportLines.add("Digital Signature: " + useDigitalSignature);
reportLines.add("Signature Text: " + (customSignatureText.isEmpty() ? "None" : customSignatureText));
reportLines.add("Date Text: " + (customDateText.isEmpty() ? "None" : customDateText));
reportLines.add(repeat("=", 60));
reportLines.add("");
// Create "Signed Documents" folder
Path signedDocumentsFolder = folder.resolve("Signed Documents");
try {
Files.createDirectories(signedDocumentsFolder);
} catch (IOException e) {
System.err.println("Warning: Could not create 'Signed Documents' folder: " + e.getMessage());
signedDocumentsFolder = folder; // Fall back to original folder
}
int totalFiles = 0;
int totalSignatureBlocks = 0;
int totalFieldsFilled = 0;
// Process each PDF file
for (Path pdfFile : pdfFiles) {
System.out.println("Processing: " + pdfFile.getFileName());
try {
ProcessingResult result = analyzePDFAndSign(pdfFile, signedDocumentsFolder, fillFields, useDigitalSignature, customSignatureText,
customDateText);
totalFiles++;
totalSignatureBlocks += result.signatureResult.signatureBlockCount;
totalFieldsFilled += result.fieldsFilledCount;
// Add results to report
reportLines.add("File: " + pdfFile.getFileName());
reportLines.add("Signature blocks found: " + result.signatureResult.signatureBlockCount);
reportLines.add("Details: " + result.signatureResult.details);
if (fillFields) {
reportLines.add("Signature inserted: " + (result.fieldsFilledCount > 0 ? "Yes" : "No"));
reportLines.add("Insert details: " + result.fieldDetails);
}
reportLines.add("");
System.out.println(" - Found " + result.signatureResult.signatureBlockCount + " signature block(s)");
if (fillFields) {
System.out.println(" - Inserted signature: " + result.fieldsFilledCount + " time(s)");
}
} catch (Exception e) {
System.err.println(" - Error processing file: " + e.getMessage());
reportLines.add("File: " + pdfFile.getFileName());
reportLines.add("ERROR: " + e.getMessage());
reportLines.add("");
}
}
// Add summary
reportLines.add(repeat("=", 60));
reportLines.add("SUMMARY");
reportLines.add("Total files processed: " + totalFiles);
reportLines.add("Total signature blocks found: " + totalSignatureBlocks);
if (fillFields) {
reportLines.add("Total signature insertions: " + totalFieldsFilled);
}
reportLines.add(
"Average signature blocks per file: " + (totalFiles > 0 ? String.format("%.2f", (double) totalSignatureBlocks / totalFiles) : "0"));
// Write report to file
Files.write(outputFile, reportLines, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
System.out.println("\nProcessing completed!");
System.out.println("Report saved to: " + outputFile.toAbsolutePath());
System.out.println("Total files processed: " + totalFiles);
System.out.println("Total signature blocks found: " + totalSignatureBlocks);
if (fillFields) {
System.out.println("Total signature insertions: " + totalFieldsFilled);
}
}
public static String repeat(String str, int count) {
if (count <= 0) {
return "";
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append(str);
}
return sb.toString();
}
/**
* Analyze a PDF file for signature blocks and apply digital or text signature
*/
private static ProcessingResult analyzePDFAndSign(Path pdfFile, Path outputFolder, boolean fillFields, boolean useDigitalSignature,
String customSignatureText, String customDateText) throws IOException {
PDDocument document = null;
boolean documentModified = false;
try {
// document = PDDocument.load(pdfFile.toFile());
document = Loader.loadPDF(pdfFile.toFile());
// Extract text for signature pattern detection
PDFTextStripper textStripper = new PDFTextStripper();
String text = textStripper.getText(document);
SignatureAnalysisResult signatureResult = detectSignatureBlocks(text);
int signatureCount = 0;
StringBuilder signatureDetails = new StringBuilder();
// Look for Deputy Executive Director and apply signature
if (fillFields && containsDeputyExecutiveDirector(text)) {
System.out.println(" Found 'Deputy Executive Director' in document");
if (useDigitalSignature) {
// Apply digital signature
signatureCount = applyDigitalSignature(document, pdfFile, customSignatureText);
if (signatureCount > 0) {
documentModified = true;
signatureDetails.append("Digital signature applied with text: '").append(customSignatureText).append("'");
} else {
signatureDetails.append("Failed to apply digital signature");
}
} else {
// Apply text signature and date to form fields
int[] results = applyTextSignatureAndDate(document, customSignatureText, customDateText);
signatureCount = results[0] + results[1]; // Total fields filled
if (signatureCount > 0) {
documentModified = true;
signatureDetails.append("Fields filled - Signature: ").append(results[0] > 0 ? "Yes" : "No").append(", Date: ")
.append(results[1] > 0 ? "Yes" : "No");
} else {
signatureDetails.append("Deputy Executive Director fields not found or could not be filled");
}
}
} else if (fillFields) {
System.out.println(" 'Deputy Executive Director' not found in document");
signatureDetails.append("Deputy Executive Director pattern not found");
}
// Save the modified document to "Signed Documents" folder
if (documentModified) {
String originalName = pdfFile.getFileName().toString();
String suffix = useDigitalSignature ? "_digitally_signed.pdf" : "_text_signed.pdf";
String newName = originalName.substring(0, originalName.lastIndexOf('.')) + suffix;
Path outputPath = outputFolder.resolve(newName);
document.save(outputPath.toFile());
System.out.println(" -> Saved modified PDF: " + outputPath.toAbsolutePath());
}
if (signatureDetails.length() == 0 && fillFields) {
signatureDetails.append("No signature applied");
}
return new ProcessingResult(signatureResult, signatureCount, signatureDetails.toString());
} finally {
if (document != null) {
try {
document.close();
} catch (IOException e) {
System.err.println("Error closing document: " + e.getMessage());
}
}
}
}
/**
* Apply digital signature to the document
*/
private static int applyDigitalSignature(PDDocument document, Path pdfFile, String customSignatureText) {
try {
// Create a self-signed certificate for testing
KeyPair keyPair = generateKeyPair();
// Create signature field if none exists
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
if (acroForm == null) {
acroForm = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(acroForm);
}
// Create digital signature
PDSignature signature = new PDSignature();
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
signature.setName(customSignatureText);
signature.setReason(SIGNATURE_REASON);
signature.setLocation(SIGNATURE_LOCATION);
signature.setSignDate(Calendar.getInstance());
// Create signature options
SignatureOptions signatureOptions = new SignatureOptions();
signatureOptions.setPreferredSignatureSize(SignatureOptions.DEFAULT_SIGNATURE_SIZE * 2);
// Apply the signature
document.addSignature(signature, new TestSignatureInterface(keyPair.getPrivate()), signatureOptions);
System.out.println(" -> Applied digital signature with text: '" + customSignatureText + "'");
return 1;
} catch (Exception e) {
System.err.println(" -> Error applying digital signature: " + e.getMessage());
return 0;
}
}
/**
* Apply both text signature and date by searching for fields with specific help text
*/
private static int[] applyTextSignatureAndDate(PDDocument document, String customSignatureText, String customDateText) {
int signatureCount = 0;
int dateCount = 0;
try {
// Apply signature
signatureCount = applyTextSignatureToDeputyField(document, customSignatureText);
// Apply date
dateCount = applyDateToDeputyField(document, customDateText);
} catch (Exception e) {
System.err.println(" -> Error applying signature and date: " + e.getMessage());
}
return new int[] { signatureCount, dateCount };
}
/**
* Apply date by searching for field with help text "Date_Deputy Executive Director"
*/
private static int applyDateToDeputyField(PDDocument document, String customDateText) {
try {
// First try to find by help text
int result = applyDateByHelpText(document, customDateText);
if (result > 0) {
return result;
}
// If help text search failed, fall back to field name search
System.out.println(" Date help text search failed, trying field name search as fallback...");
return applyDateToDeputyFieldByName(document, customDateText);
} catch (Exception e) {
System.err.println(" -> Error in date application: " + e.getMessage());
return 0;
}
}
/**
* Apply date by finding field with specific help text "Date_Deputy Executive Director"
*/
private static int applyDateByHelpText(PDDocument document, String customDateText) {
try {
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
if (acroForm != null) {
System.out.println(" Searching through " + acroForm.getFields().size()
+ " form field(s) for field with help text 'Date_Deputy Executive Director'");
// Search for field with matching help text
for (PDField field : acroForm.getFields()) {
String fieldName = field.getFullyQualifiedName();
String helpText = getFieldHelpText(field);
System.out.println(" Checking field: '" + fieldName +
"' - Help text: '" + (helpText == null ? "null" : helpText) + "' (Type: "
+ field.getFieldType() + ")");
// Check if this field has the target date help text and is a text field
if (field instanceof PDTextField && isTargetDateHelpText(helpText)) {
PDTextField textField = (PDTextField) field;
String currentValue = textField.getValue();
System.out.println(" -> FOUND TARGET DATE FIELD BY HELP TEXT: '" + fieldName + "'");
System.out.println(" -> Help text: '" + helpText + "'");
System.out.println(" -> Current value: '" + (currentValue == null ? "null" : currentValue) + "'");
// Fill the field with the date
textField.setValue(customDateText);
System.out.println(" -> SUCCESS: Updated date field '" + fieldName + "' with: '" + customDateText + "'");
return 1;
}
}
System.out.println(" No field found with help text 'Date_Deputy Executive Director'");
} else {
System.out.println(" -> No form fields found in PDF");
}
return 0;
} catch (Exception e) {
System.err.println(" -> Error applying date by help text: " + e.getMessage());
e.printStackTrace();
return 0;
}
}
/**
* Apply date by searching field names for Deputy Executive Director date field
*/
private static int applyDateToDeputyFieldByName(PDDocument document, String customDateText) {
try {
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
if (acroForm != null) {
System.out.println(" Searching by field name for Deputy Executive Director date field");
// Look for date fields by name
for (PDField field : acroForm.getFields()) {
String fieldName = field.getFullyQualifiedName();
// Check for date field variations
if (field instanceof PDTextField && isDeputyDateField(fieldName)) {
PDTextField textField = (PDTextField) field;
String currentValue = textField.getValue();
System.out.println(" -> FOUND TARGET DATE FIELD BY NAME: '" + fieldName + "'");
System.out.println(" -> Current value: '" + (currentValue == null ? "null" : currentValue) + "'");
textField.setValue(customDateText);
System.out.println(" -> SUCCESS: Updated date field '" + fieldName + "' with: '" + customDateText + "'");
return 1;
}
}
}
System.out.println(" -> No date field found by name");
return 0;
} catch (Exception e) {
System.err.println(" -> Error in date field name search: " + e.getMessage());
return 0;
}
}
/**
* Check if the help text matches our target date field
*/
private static boolean isTargetDateHelpText(String helpText) {
if (helpText == null || helpText.trim().isEmpty()) {
return false;
}
String text = helpText.trim();
// Exact match
if (text.equalsIgnoreCase("Date_Deputy Executive Director")) {
return true;
}
// Close variations
return text.equalsIgnoreCase("Date Deputy Executive Director") || text.equalsIgnoreCase("Date_Deputy_Executive_Director")
|| text.equalsIgnoreCase("DateDeputyExecutiveDirector")
|| (text.toLowerCase().contains("date") && text.toLowerCase().contains("deputy") && text.toLowerCase().contains("executive")
&& text.toLowerCase().contains("director"));
}
/**
* Check if field name suggests it's a date field for Deputy Executive Director
*/
private static boolean isDeputyDateField(String fieldName) {
String name = fieldName.toLowerCase().trim();
// Must contain "date" AND ("deputy" OR "executive" OR "director")
boolean hasDate = name.contains("date");
boolean hasDeputy = name.contains("deputy");
boolean hasExecutive = name.contains("executive");
boolean hasDirector = name.contains("director");
return hasDate && (hasDeputy || (hasExecutive && hasDirector));
}
/**
* Validate date format MM/dd/yyyy
*/
private static boolean isValidDateFormat(String date) {
if (date == null || date.trim().isEmpty()) {
return false;
}
// Check basic format with regex
String datePattern = "^(0[1-9]|1[0-2])/(0[1-9]|[12][0-9]|3[01])/\\d{4}$";
if (!date.matches(datePattern)) {
return false;
}
try {
// Additional validation - try to parse the date
String[] parts = date.split("/");
int month = Integer.parseInt(parts[0]);
int day = Integer.parseInt(parts[1]);
int year = Integer.parseInt(parts[2]);
// Basic range checks
if (month < 1 || month > 12) {
return false;
}
if (day < 1 || day > 31) {
return false;
}
if (year < 1900 || year > 2100) {
return false; // Reasonable year range
}
// Check days per month (simplified)
if ((month == 4 || month == 6 || month == 9 || month == 11) && day > 30) {
return false;
}
if (month == 2 && day > 29) {
return false;
}
return true;
} catch (NumberFormatException e) {
return false;
}
}
/**
* Apply text signature by searching for field with help text or field name
*/
private static int applyTextSignatureToDeputyField(PDDocument document, String customSignatureText) {
try {
// First try to find by help text
int result = applyTextSignatureByHelpText(document, customSignatureText);
if (result > 0) {
return result;
}
// If help text search failed, fall back to original field name search
System.out.println(" Signature help text search failed, trying field name search as fallback...");
return applyTextSignatureToDeputyFieldByName(document, customSignatureText);
} catch (Exception e) {
System.err.println(" -> Error in signature application: " + e.getMessage());
return 0;
}
}
private static int applyTextSignatureByHelpText(PDDocument document, String customSignatureText) {
try {
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
if (acroForm != null) {
System.out.println(" Searching through " + acroForm.getFields().size()
+ " form field(s) for field with help text 'Signature_Deputy Executive Director'");
// Search for field with matching help text
for (PDField field : acroForm.getFields()) {
String fieldName = field.getFullyQualifiedName();
String helpText = getFieldHelpText(field);
System.out.println(" Checking field: '" + fieldName + "' - Help text: '" + (helpText == null ? "null" : helpText) + "' (Type: "
+ field.getFieldType() + ")");
// Check if this field has the target help text and is a text field
if (field instanceof PDTextField && isTargetHelpText(helpText)) {
PDTextField textField = (PDTextField) field;
String currentValue = textField.getValue();
System.out.println(" -> FOUND TARGET FIELD BY HELP TEXT: '" + fieldName + "'");
System.out.println(" -> Help text: '" + helpText + "'");
System.out.println(" -> Current value: '" + (currentValue == null ? "null" : currentValue) + "'");
// Fill the field with the signature
textField.setValue(customSignatureText);
System.out.println(" -> SUCCESS: Updated field '" + fieldName + "' with: '" + customSignatureText + "'");
return 1;
}
}
// If no field found by help text, list all fields with their help text for debugging
System.out.println(" No field found with help text 'Signature_Deputy Executive Director'. Available fields and their help text:");
for (PDField field : acroForm.getFields()) {
String fieldName = field.getFullyQualifiedName();
String fieldType = field.getFieldType();
String helpText = getFieldHelpText(field);
String currentValue = "";
if (field instanceof PDTextField) {
try {
String val = ((PDTextField) field).getValue();
currentValue = " [Current: '" + (val == null ? "empty" : val) + "']";
} catch (Exception e) {
currentValue = " [Error reading value]";
}
}
System.out.println(" - Field: '" + fieldName + "' (Type: " + fieldType + ")" + " Help: '"
+ (helpText == null ? "none" : helpText) + "'" + currentValue);
}
} else {
System.out.println(" -> No form fields found in PDF");
}
System.out.println(" -> No field found with help text 'Signature_Deputy Executive Director'");
return 0;
} catch (Exception e) {
System.err.println(" -> Error applying text signature by help text: " + e.getMessage());
e.printStackTrace();
return 0;
}
}
/**
* Extract help text (tooltip) from a PDF form field
*/
private static String getFieldHelpText(PDField field) {
try {
// Try to get the tooltip/help text from various possible sources
// Method 1: Try to get from field's dictionary using common keys
if (field.getCOSObject() != null) {
// Check for "TU" (User) which is typically used for tooltips
String tuValue = field.getCOSObject().getString("TU");
if (tuValue != null && !tuValue.trim().isEmpty()) {
return tuValue.trim();
}
// Check for "T" (Title) as fallback
String tValue = field.getCOSObject().getString("T");
if (tValue != null && !tValue.trim().isEmpty()) {
return tValue.trim();
}
}
// Method 2: Try alternative name
String alternativeName = field.getAlternateFieldName();
if (alternativeName != null && !alternativeName.trim().isEmpty()) {
return alternativeName.trim();
}
// Method 3: Try mapped name
String mappingName = field.getMappingName();
if (mappingName != null && !mappingName.trim().isEmpty()) {
return mappingName.trim();
}
return null;
} catch (Exception e) {
System.err.println(" Error reading help text for field: " + e.getMessage());
return null;
}
}
/**
* Check if the help text matches our target
*/
private static boolean isTargetHelpText(String helpText) {
if (helpText == null || helpText.trim().isEmpty()) {
return false;
}
String text = helpText.trim();
// Exact match
if (text.equalsIgnoreCase("Signature_Deputy Executive Director")) {
return true;
}
// Close variations
return text.equalsIgnoreCase("Signature Deputy Executive Director") || text.equalsIgnoreCase("Signature_Deputy_Executive_Director")
|| text.equalsIgnoreCase("SignatureDeputyExecutiveDirector")
|| text.toLowerCase().contains("signature") && text.toLowerCase().contains("deputy") && text.toLowerCase().contains("executive")
&& text.toLowerCase().contains("director");
}
/**
* Original field name search method (renamed for clarity)
*/
private static int applyTextSignatureToDeputyFieldByName(PDDocument document, String customSignatureText) {
try {
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
if (acroForm != null) {
System.out.println(" Searching by field name for 'Signature_Deputy Executive Director' field");
// Look specifically for "Signature_Deputy Executive Director" field by name
for (PDField field : acroForm.getFields()) {
String fieldName = field.getFullyQualifiedName();
// Check for exact match or close variations
if (field instanceof PDTextField && isExactDeputySignatureField(fieldName)) {
PDTextField textField = (PDTextField) field;
String currentValue = textField.getValue();
System.out.println(" -> FOUND TARGET FIELD BY NAME: '" + fieldName + "'");
System.out.println(" -> Current value: '" + (currentValue == null ? "null" : currentValue) + "'");
textField.setValue(customSignatureText);
System.out.println(" -> SUCCESS: Updated '" + fieldName + "' with: '" + customSignatureText + "'");
return 1;
}
}
// Look for partial matches if exact field not found
for (PDField field : acroForm.getFields()) {
String fieldName = field.getFullyQualifiedName();
if (field instanceof PDTextField && isPartialDeputySignatureField(fieldName)) {
PDTextField textField = (PDTextField) field;
textField.setValue(customSignatureText);
System.out.println(" -> SUCCESS: Updated similar field '" + fieldName + "' with: '" + customSignatureText + "'");
return 1;
}
}
// List all available fields for debugging
System.out.println(" No Deputy Executive Director signature field found by name. Available fields:");
for (PDField field : acroForm.getFields()) {
String fieldName = field.getFullyQualifiedName();
String fieldType = field.getFieldType();
String currentValue = "";
if (field instanceof PDTextField) {
try {
String val = ((PDTextField) field).getValue();
currentValue = " [Current: '" + (val == null ? "empty" : val) + "']";
} catch (Exception e) {
currentValue = " [Error reading value]";
}
}
System.out.println(" - '" + fieldName + "' (Type: " + fieldType + ")" + currentValue);
}
}
System.out.println(" -> No field found by name");
return 0;
} catch (Exception e) {
System.err.println(" -> Error in field name search: " + e.getMessage());
return 0;
}
}
/**
* Check if field is exactly the "Signature_Deputy Executive Director" field
*/
private static boolean isExactDeputySignatureField(String fieldName) {
// Check for exact match (case insensitive)
String name = fieldName.trim();
return name.equalsIgnoreCase("Signature_Deputy Executive Director") || name.equalsIgnoreCase("Signature_Deputy_Executive_Director")
|| name.equalsIgnoreCase("SignatureDeputyExecutiveDirector") || name.equalsIgnoreCase("Signature Deputy Executive Director");
}
/**
* Check if field is a partial match for Deputy Executive Director signature field
*/
private static boolean isPartialDeputySignatureField(String fieldName) {
String name = fieldName.toLowerCase().trim();
// Must contain "signature" AND ("deputy" OR "executive" OR "director")
boolean hasSignature = name.contains("signature") || name.contains("sign");
boolean hasDeputy = name.contains("deputy");
boolean hasExecutive = name.contains("executive");
boolean hasDirector = name.contains("director");
return hasSignature && (hasDeputy || (hasExecutive && hasDirector));
}
/**
* Generate a key pair for digital signing (for testing purposes)
*/
private static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048, new SecureRandom());
return keyGen.generateKeyPair();
}
/**
* Simple signature interface for testing
*/
private static class TestSignatureInterface implements SignatureInterface {
private final PrivateKey privateKey;
public TestSignatureInterface(PrivateKey privateKey) {
this.privateKey = privateKey;
}
@Override
public byte[] sign(InputStream content) throws IOException {
try {
// Create a simple signature for testing
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(this.privateKey);
// Read content and sign it
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = content.read(buffer)) != -1) {
signature.update(buffer, 0, bytesRead);
}
return signature.sign();
} catch (Exception e) {
throw new IOException("Error creating signature", e);
}
}
}
/**
* Check if the text contains Deputy Executive Director
*/
private static boolean containsDeputyExecutiveDirector(String text) {
return text.toLowerCase().contains("deputy executive director");
}
/**
* Check if a field name suggests it's related to Deputy Executive Director
*/
private static boolean isDeputyExecutiveDirectorField(String fieldName) {
String name = fieldName.toLowerCase();
return name.contains("deputy") || name.contains("executive") || name.contains("director") || name.contains("sign")
|| name.contains("signature") || (name.contains("name") && !name.contains("filename")) || name.contains("auth")
|| name.matches(".*field[0-9]*$") || // Generic field names like "field1", "field2"
name.matches(".*text[0-9]*$"); // Generic text field names
}
/**
* Detect signature blocks in the extracted text using pattern matching
*/
private static SignatureAnalysisResult detectSignatureBlocks(String text) {
Set<String> foundPatterns = new HashSet<>();
int totalMatches = 0;
for (Pattern pattern : SIGNATURE_PATTERNS) {
Matcher matcher = pattern.matcher(text);
int matches = 0;
while (matcher.find()) {
matches++;
totalMatches++;
foundPatterns.add(pattern.pattern());
}
if (matches > 0) {
System.out.println(" Pattern '" + pattern.pattern() + "' matched " + matches + " time(s)");
}
}
// Create details string
StringBuilder details = new StringBuilder();
if (foundPatterns.isEmpty()) {
details.append("No signature patterns detected");
} else {
details.append("Detected patterns: ");
details.append(String.join(", ", foundPatterns));
}
return new SignatureAnalysisResult(totalMatches, details.toString());
}
/**
* Result class to hold signature analysis data
*/
private static class SignatureAnalysisResult {
final int signatureBlockCount;
final String details;
SignatureAnalysisResult(int count, String details) {
this.signatureBlockCount = count;
this.details = details;
}
}
/**
* Result class to hold processing results
*/
private static class ProcessingResult {
final SignatureAnalysisResult signatureResult;
final int fieldsFilledCount;
final String fieldDetails;
ProcessingResult(SignatureAnalysisResult signatureResult, int fieldsFilledCount, String fieldDetails) {
this.signatureResult = signatureResult;
this.fieldsFilledCount = fieldsFilledCount;
this.fieldDetails = fieldDetails;
}
}
}
To run this program, either we can use Run As -> Java application in Eclipse
or
Export it as jar file, go to project-> right click -> click export -> select java , export as runnable jar.
update location to save jar and name.
output PDF:
Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies> <!-- Main PDFBox library -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.29</version>
</dependency> <!-- PDFBox tools for
additional functionality -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox-tools</artifactId>
<version>2.0.29</version>
</dependency> <!-- FontBox for font
handling -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>fontbox</artifactId>
<version>2.0.29</version>
</dependency> <!-- XmpBox for XMP
metadata support -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>xmpbox</artifactId>
<version>2.0.29</version>
</dependency> <!-- PrefLight for
PDF/A validation -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>preflight</artifactId>
<version>2.0.29</version>
</dependency>
</dependencies>
</project>
No comments:
Post a Comment