How to search signatures with predicate functions

Search with Predicate Functions

Overview

The Signature class of GroupDocs.Signature provides search methods that accept predicate functions (java.util.function.Predicate) to filter signatures based on custom criteria. This allows you to perform more flexible and targeted signature searches by applying custom filtering logic to the search results.

Benefits

  • Flexible Filtering: Filter signatures based on any custom criteria using lambda expressions
  • Type-Safe: Strongly typed predicates ensure compile-time safety
  • Easy to Use: Simple lambda expressions make filtering intuitive
  • Comprehensive Results: Search across all signature types or specific types, then filter as needed

Available Methods

  1. search(List<SearchOptions> searchOptionsList, Predicate<BaseSignature> predicate)
  2. search(Predicate<BaseSignature> predicate)
  3. search(Class<T> typeOfT, SearchOptions searchOptions, Predicate<T> predicate)

Method Signatures

public final SearchResult search(List<SearchOptions> searchOptionsList, Predicate<BaseSignature> predicate) throws Exception
public final List<BaseSignature> search(Predicate<BaseSignature> predicate) throws Exception
public final <T extends BaseSignature> List<T> search(Class<T> typeOfT, SearchOptions searchOptions, Predicate<T> predicate) throws Exception

How It Works

The search methods with predicates:

  1. Search First: All signatures matching the search options are found. search(Predicate) takes no options and searches for text, image, digital, barcode, QR-code and metadata signatures.
  2. Filter Results: The predicate is applied to filter the search results
  3. Return Filtered Results: Only signatures matching the predicate are returned

The predicate receives a BaseSignature. Use instanceof and a cast to read the properties of a specific signature class, such as TextSignature or QrCodeSignature.

Examples

Example 1: Search and Filter by Text Content

Signature signature = new Signature("document.pdf");
try
{
    // Search for text signatures
    List<SearchOptions> searchOptions = new ArrayList<SearchOptions>();
    searchOptions.add(new TextSearchOptions());

    // Filter to only signatures containing "Approved"
    SearchResult result = signature.search(searchOptions,
        sig -> sig instanceof TextSignature && ((TextSignature) sig).getText().contains("Approved"));

    for (BaseSignature sig : result.getSignatures())
    {
        System.out.println("Found: " + ((TextSignature) sig).getText());
    }
}
finally
{
    signature.dispose();
}

Example 2: Search All Signature Types with Filtering

Signature signature = new Signature("document.pdf");
try
{
    // Search all signature types and filter by type
    List<BaseSignature> result = signature.search(
        sig -> sig.getSignatureType() == SignatureType.Digital);

    System.out.println("Found " + result.size() + " digital signatures");
}
finally
{
    signature.dispose();
}

Example 3: Search and Filter by Multiple Criteria

Signature signature = new Signature("document.pdf");
try
{
    List<SearchOptions> searchOptions = new ArrayList<SearchOptions>();
    searchOptions.add(new TextSearchOptions());
    searchOptions.add(new BarcodeSearchOptions());

    // Filter signatures by page and type
    SearchResult result = signature.search(searchOptions, sig ->
        sig.getPageNumber() == 1 &&
        (sig.getSignatureType() == SignatureType.Text || sig.getSignatureType() == SignatureType.Barcode));

    System.out.println("Found " + result.getSignatures().size() + " signatures on page 1");
}
finally
{
    signature.dispose();
}

Example 4: Search Digital Signatures by Sign Time

Signature signature = new Signature("document.pdf");
try
{
    Date startDate = new GregorianCalendar(2024, Calendar.JANUARY, 1).getTime();
    Date endDate = new GregorianCalendar(2024, Calendar.DECEMBER, 31, 23, 59, 59).getTime();

    // Search all signatures and keep digital signatures signed in 2024
    List<BaseSignature> result = signature.search(sig ->
    {
        if (!(sig instanceof DigitalSignature))
        {
            return false;
        }
        Date signTime = ((DigitalSignature) sig).getSignTime();
        return signTime != null && !signTime.before(startDate) && !signTime.after(endDate);
    });

    System.out.println("Found " + result.size() + " signatures in 2024");
}
finally
{
    signature.dispose();
}

Example 5: Search and Filter by Signature Size

Signature signature = new Signature("document.pdf");
try
{
    List<SearchOptions> searchOptions = new ArrayList<SearchOptions>();
    searchOptions.add(new ImageSearchOptions());

    // Filter image signatures by size
    SearchResult result = signature.search(searchOptions, sig ->
    {
        if (sig instanceof ImageSignature)
        {
            ImageSignature imgSig = (ImageSignature) sig;
            return imgSig.getWidth() > 200 && imgSig.getHeight() > 200;
        }
        return false;
    });

    System.out.println("Found " + result.getSignatures().size() + " large image signatures");
}
finally
{
    signature.dispose();
}

Example 6: Find Signatures in Specific Region

Signature signature = new Signature("document.pdf");
try
{
    // Define a region (rectangle)
    int regionLeft = 100;
    int regionTop = 100;
    int regionWidth = 200;
    int regionHeight = 200;

    List<BaseSignature> result = signature.search(sig ->
        sig.getLeft() >= regionLeft &&
        sig.getTop() >= regionTop &&
        sig.getLeft() + sig.getWidth() <= regionLeft + regionWidth &&
        sig.getTop() + sig.getHeight() <= regionTop + regionHeight);

    System.out.println("Found " + result.size() + " signatures in the specified region");
}
finally
{
    signature.dispose();
}

Example 7: Search Signatures with Specific Properties

Signature signature = new Signature("document.pdf");
try
{
    List<SearchOptions> searchOptions = new ArrayList<SearchOptions>();
    searchOptions.add(new QrCodeSearchOptions());

    // Search for QR codes with specific data
    SearchResult result = signature.search(searchOptions, sig ->
    {
        if (sig instanceof QrCodeSignature)
        {
            String text = ((QrCodeSignature) sig).getText();
            return text != null && (text.startsWith("https://") || text.contains("contract-id:"));
        }
        return false;
    });

    for (QrCodeSignature qrSig : result.toList(QrCodeSignature.class))
    {
        System.out.println("QR Code: " + qrSig.getText());
    }
}
finally
{
    signature.dispose();
}

Example 8: Search by Signature Type and Page

Signature signature = new Signature("document.pdf");
try
{
    // Search all signatures and filter by type and page
    List<BaseSignature> result = signature.search(sig ->
        sig.getSignatureType() == SignatureType.Text &&
        sig.getPageNumber() == 1);

    System.out.println("Found " + result.size() + " text signatures on page 1");
}
finally
{
    signature.dispose();
}

Example 9: Search a Single Signature Type with a Typed Predicate

The typed overload passes the signature class, so the predicate gets that class directly and no cast is needed:

// Search QR-code signatures and keep only those on the first page
QrCodeSearchOptions options = new QrCodeSearchOptions();
List<QrCodeSignature> signatures = signature.search(QrCodeSignature.class, options,
    qrSig -> qrSig.getPageNumber() == 1 && qrSig.getText() != null);

for (QrCodeSignature qrSig : signatures)
{
    System.out.println("QR Code on page 1: " + qrSig.getText());
}

Common Use Cases

1. Find Signatures by Author

Signature signature = new Signature("document.pdf");
try
{
    List<SearchOptions> searchOptions = new ArrayList<SearchOptions>();
    searchOptions.add(new DigitalSearchOptions());

    // Keep digital signatures whose certificate subject contains "John Doe"
    SearchResult result = signature.search(searchOptions, sig ->
    {
        if (!(sig instanceof DigitalSignature))
        {
            return false;
        }
        X509Certificate certificate = ((DigitalSignature) sig).getX509Certificate();
        return certificate != null &&
               certificate.getSubjectX500Principal().getName().contains("John Doe");
    });

    System.out.println("Found " + result.getSignatures().size() + " signatures by John Doe");
}
finally
{
    signature.dispose();
}

2. Search Signatures by Barcode Value

Signature signature = new Signature("document.pdf");
try
{
    List<SearchOptions> searchOptions = new ArrayList<SearchOptions>();
    searchOptions.add(new BarcodeSearchOptions());

    SearchResult result = signature.search(searchOptions,
        sig -> sig instanceof BarcodeSignature &&
               ((BarcodeSignature) sig).getText().startsWith("PROD-"));

    for (BarcodeSignature barcodeSig : result.toList(BarcodeSignature.class))
    {
        System.out.println("Product Barcode: " + barcodeSig.getText());
    }
}
finally
{
    signature.dispose();
}

3. Find Signatures by Position and Size

Signature signature = new Signature("document.pdf");
try
{
    // Find signatures in bottom-right corner that are large
    List<BaseSignature> result = signature.search(sig ->
        sig.getLeft() > 400 &&
        sig.getTop() > 600 &&
        sig.getWidth() > 150 &&
        sig.getHeight() > 50);

    System.out.println("Found " + result.size() + " large signatures in bottom-right");
}
finally
{
    signature.dispose();
}

4. Search Multiple Signature Types with Complex Filtering

Signature signature = new Signature("document.pdf");
try
{
    List<SearchOptions> searchOptions = new ArrayList<SearchOptions>();
    searchOptions.add(new TextSearchOptions());
    searchOptions.add(new ImageSearchOptions());
    searchOptions.add(new QrCodeSearchOptions());

    // Find signatures that are either text with specific content,
    // or QR codes with URLs, or large images
    SearchResult result = signature.search(searchOptions, sig ->
    {
        if (sig instanceof TextSignature)
        {
            return ((TextSignature) sig).getText().contains("APPROVED");
        }
        if (sig instanceof QrCodeSignature)
        {
            return ((QrCodeSignature) sig).getText().startsWith("http");
        }
        if (sig instanceof ImageSignature)
        {
            return sig.getWidth() > 300 && sig.getHeight() > 300;
        }
        return false;
    });

    System.out.println("Found " + result.getSignatures().size() + " matching signatures");
}
finally
{
    signature.dispose();
}

Best Practices

1. Use Specific Predicates

Good:

// Specific and clear predicate
SearchResult specific = signature.search(searchOptions,
    sig -> sig instanceof TextSignature && "Approved".equals(((TextSignature) sig).getText()));

Avoid:

// Too generic, returns every signature of every type
List<BaseSignature> everything = signature.search(sig -> true);

2. Handle Null Checks

// Safe predicate with null checks
SearchResult safe = signature.search(searchOptions, sig ->
{
    if (sig == null) return false;
    if (sig instanceof TextSignature)
    {
        String text = ((TextSignature) sig).getText();
        return text != null && !text.isEmpty() && text.contains("Contract");
    }
    return false;
});

3. Combine Multiple Conditions

// Clear, readable predicate with multiple conditions
SearchResult combined = signature.search(searchOptions, sig ->
    sig.getPageNumber() == 1 &&
    sig.getSignatureType() == SignatureType.Text &&
    sig instanceof TextSignature &&
    ((TextSignature) sig).getText().length() > 10);

4. Reusable Predicates

java.util.function.Predicate provides the and, or and negate methods to combine predicates:

// Define reusable predicates
Predicate<BaseSignature> isOnFirstPage = sig -> sig.getPageNumber() == 1;
Predicate<BaseSignature> isTextSignature = sig -> sig.getSignatureType() == SignatureType.Text;
Predicate<BaseSignature> isLargeSignature = sig -> sig.getWidth() > 200 && sig.getHeight() > 200;

// Combine predicates
SearchResult result = signature.search(searchOptions,
    isOnFirstPage.and(isTextSignature).and(isLargeSignature));

5. Use Appropriate Search Options

// Use specific search options to narrow down the initial search
TextSearchOptions textOptions = new TextSearchOptions("Contract");
List<SearchOptions> searchOptions = new ArrayList<SearchOptions>();
searchOptions.add(textOptions);

// Then apply additional filtering with the predicate
SearchResult result = signature.search(searchOptions,
    sig -> sig.getPageNumber() == 1);

Error Handling

Null Parameter Validation

search(Predicate) requires a predicate: passing null throws an unchecked exception with the message “Value cannot be null. Parameter name: predicate”. The overloads that take search options treat a null predicate as “no filter” and return all found signatures.

Signature signature = new Signature("document.pdf");
try
{
    // A null predicate is rejected by search(Predicate)
    List<BaseSignature> result = signature.search((Predicate<BaseSignature>) null);
}
catch (RuntimeException ex)
{
    System.out.println("Error: " + ex.getMessage());
}
finally
{
    signature.dispose();
}

Return Values

  • search(List<SearchOptions>, Predicate<BaseSignature>): Returns SearchResult. Its getSignatures() list holds the filtered signatures. The statistics of the search, such as getProcessingTime(), are not filled in for this filtered result.
  • search(Predicate<BaseSignature>): Returns List<BaseSignature> - A list of filtered signatures
  • search(Class<T>, SearchOptions, Predicate<T>): Returns List<T> - A typed list of filtered signatures
  • Empty Results: Returns empty collection if no signatures match the predicate

Working with SearchResult

SearchResult result = signature.search(searchOptions, predicate);

// Access filtered signatures
List<BaseSignature> signatures = result.getSignatures();

// Convert to a typed list
List<TextSignature> textSignatures = result.toList(TextSignature.class);

toList converts every item of the result to the requested class and puts null in place of an item of another class, so use it when the search options cover a single signature type.

Comparison: With vs Without Predicate

Without Predicate

// Get all search results
SearchResult result = signature.search(searchOptions);
List<BaseSignature> allSignatures = result.getSignatures();

// Manual filtering
List<BaseSignature> filtered = allSignatures.stream()
    .filter(sig -> sig.getPageNumber() == 1)
    .collect(Collectors.toList());

With Predicate

// Filtered during search (more concise)
SearchResult result = signature.search(searchOptions,
    sig -> sig.getPageNumber() == 1);
List<BaseSignature> filtered = result.getSignatures();

Advantages:

  • More concise code
  • Cleaner API
  • Single method call instead of search + filter

Summary

The predicate-based search methods provide a powerful and flexible way to search for signatures in GroupDocs.Signature. They allow you to:

  • Filter signatures based on any custom criteria
  • Search across all signature types or specific types
  • Write more readable and maintainable code
  • Handle complex filtering scenarios with ease

Use these methods when you need to find a subset of signatures based on specific criteria, rather than searching all signatures and filtering manually.

More resources

GitHub Examples

You may easily run the code above and see the feature in action in our GitHub examples:

Free Online Apps

Along with the full-featured Java library, we provide simple but powerful free online apps.

To sign PDF, Word, Excel, PowerPoint, and other documents you can use the online apps from the GroupDocs.Signature App Product Family.

Close
Loading

Analyzing your prompt, please hold on...

An error occurred while retrieving the results. Please refresh the page and try again.