GroupDocs.Signature for Java 23.12 Release Notes

In this release, we are excited to announce support of the multi-layers DICOM files support. New product features allow to process and manage DICOM documents with new signatures, obtain file information, and provide searching for signature artifacts. This development will enhance the usability of our product in a variety of healthcare, and medical imaging applications. There are about 9 features, 3 enhancements and 13 bug fixes in this release.

Full list of changes in this release

KeyCategorySummary
SIGNATURENET-4530★ FeatureImplement searching for signatures within the Archives
SIGNATURENET-4559★ FeatureImplement searching DICOM image documents for signatures
SIGNATURENET-4557★ FeatureImplement obtaining DICOM image documents information
SIGNATURENET-4501★ FeatureImplement signing DICOM image documents
SIGNATURENET-4372★ FeatureImplement Searching in the Certificate documents
SIGNATURENET-4605★ FeatureImplement verification DICOM image documents for signatures
SIGNATURENET-4574★ FeatureImplement preview for DICOM image documents
SIGNATURENET-4595★ FeatureRespect saving XmpData package for DICOM
SIGNATURENET-4598★ FeatureImplement gathering extended DICOM XmpData into the DocumentInfo
SIGNATURENET-4537★ EnhancementExtend DocumentResultSignature with the Error message
SIGNATURENET-4532★ EnhancementProvide signing Spreadsheets by using digital signature as container for certificates
SIGNATURENET-4602★ EnhancementProvide size and position for image search result Barcode and QrCode signatures
SIGNATURENET-4533🔧 FixThe output signed archive contains trial limitations
SIGNATURENET-4531🔧 FixUnexpected border line appearance of Stamp signature when corners set at too big values
SIGNATURENET-4510🔧 FixLine background overlaps border of Stamp signature result
SIGNATURENET-4509🔧 FixThe different border widths of the Stamp Signature appearance
SIGNATURENET-4508🔧 FixUnexpected visual artifacts for Stamp signature appearance
SIGNATURENET-4487🔧 FixWord processing documents are not signed within the archive files
SIGNATURENET-4548🔧 FixDigital certificate issue with signing under Linux
SIGNATURENET-4505🔧 FixSome two-pages WordProcessing documents could not be signed at trial mode
SIGNATURENET-4486🔧 FixDigital certificate for Spreadsheets has wrong format
SIGNATURENET-4481🔧 FixAspose.Word 23.1 Digitally signed .ODT document raises exception during digital signatures deletion
SIGNATURENET-4432🔧 FixDigitally signed Open Office .ODT document raises exception during digital signatures deletion
SIGNATURENET-4599🔧 FixSearch processing does not give expected results due to evaluation message in internal output images
SIGNATURENET-4504🔧 FixWrong signature position for documents signed in archives

Major Features

This release includes four new archive features and one enhancement:

Implement searching for signatures within the Archives

🌐 The existing class DocumentResultSignature will keep Search sigantures for the result of the particular processed document.

/// <summary>
/// Implement searching for signatures within the Archives
/// </summary>
Signature signature = new Signature(filePath);
{
    // create list of signature options
    BarcodeSearchOptions bcOptions = new BarcodeSearchOptions(BarcodeTypes.Code128);
    QrCodeSearchOptions qrOptions = new QrCodeSearchOptions(QrCodeTypes.QR);

    // setup search options
    List<SearchOptions> listOptions = new ArrayList<SearchOptions>();
    listOptions.add(bcOptions);
    listOptions.add(qrOptions);

    // search archive for documents
    SearchResult searchResult = signature.search(listOptions);

    // check the result                
    System.out.print("\nList of successfully processed documents:");
    int number = 1;
    for (BaseSignature o : searchResult.getSucceeded())
    {
        DocumentResultSignature document = (DocumentResultSignature)o;
        System.out.print("Document #"+number++ +": "+document.getFileName()+". Processed: "+document.getProcessingTime()+", mls");
        for (BaseSignature temp : document.getSucceeded())
        {
            System.out.print("\t\t#"+temp.getSignatureId()+": "+temp.getSignatureType());
        }
    }
    if (searchResult.getFailed().size() > 0)
    {
        System.out.print("\nList of failed documents:");
        number = 1;
        for (BaseSignature o : searchResult.getFailed())
        {
            DocumentResultSignature document = (DocumentResultSignature)o;
            System.out.print("ERROR in Document #"+number++ +"-"+document.getFileName()+": "+document.getErrorMessage()+", mls");
        }
    }
}

Implement searching DICOM image documents for signatures

🌐 The searching process within the DICOM documents does not look different than searching for single-page raster image, except for the fact that all result images will contain the page number of the layer where the signature was found.

/// <summary>
/// Search multi-layer image document for signatures.
/// This example provided for formats like DICOM
/// </summary>
Signature signature = new Signature("signed.dcm");
{
    // setup search options
    QrCodeSearchOptions searchOptions = new QrCodeSearchOptions();
    {
        // enable grabbing image content feature
        searchOptions.setReturnContent(true);
        // specify exact image type to be returned
        searchOptions.setReturnContentType(FileType.PNG);
    };
    // search multi-layer document
    List<QrCodeSignature> signatures = signature.search(QrCodeSignature.class, searchOptions);
    System.out.print("\nSource document ['"+fileName+"'] contains following QR-code signature(s).");
    // output signatures
    for (QrCodeSignature qrSignature : signatures)
    {
        // due to multi-layers each signature will contain the page number
        System.out.print("Found Qr-Code "+qrSignature.getText()+" signature at page "+qrSignature.getPageNumber()+" and id# "+qrSignature.getSignatureId()+".");
        System.out.print("Location at "+qrSignature.getLeft()+"-"+qrSignature.getTop()+". Size is "+qrSignature.getWidth()+"x"+qrSignature.getHeight()+".");
    }
}

Implement obtaining DICOM image documents information

🌐 How to obtain the DICOM image information - this is a functionality that provides users with easy access to vital details embedded within the DICOM files. This includes each image page information and its size.

/// <summary>
/// Obtain information from multi-layer image document.
/// This example provided for formats like DICOM
/// </summary>
Signature signature = new Signature(filePath);
{
    IDocumentInfo documentInfo = signature.getDocumentInfo();
    System.out.print("Document properties "+ filePath);
    System.out.print(" - format : "+documentInfo.getFileType().getFileFormat());
    System.out.print(" - extension : "+documentInfo.getFileType().getExtension());
    System.out.print(" - size : "+documentInfo.getSize());
    System.out.print(" - page count : "+documentInfo.getPageCount());
    for (PageInfo pageInfo : documentInfo.getPages())
    {
        System.out.print(" - page-"+pageInfo.getPageNumber()+" Width "+pageInfo.getWidth()+", Height "+pageInfo.getHeight());
    }
}

Implement signing DICOM image documents

🌐 Public API to sign the multilayer image documents is the same as signing any other file format. The output SignResult instance will contains the newly added signature objects with the PageNumber equal to image layer index + 1. So the setPageNumber property for the multilayer images will keep values from 1 (not 0-based).

/// <summary>
/// Sign multi-layer image document.
/// </summary>
Signature signature = new Signature("sample.dcm");
{
    QrCodeSignOptions options = new QrCodeSignOptions("Patient #36363393. R: No-Issues");
    {
        Padding padding = new  Padding();
        // set signature position 
        padding.setRight(5);
        padding.setLeft(5);
        options.setMargin(tmp0);
        // set signature rectangle
        options.setWidth(100);
        options.setHeight(100);
    };
    // sign document to file
    SignResult signResult = signature.sign(outputFilePath, options);
    System.out.print("\nSource document signed successfully with "+signResult.getSucceeded().size()+" signature(s).\nFile saved at "+outputFilePath);
    System.out.print("\nList of newly created signatures:");
    for (BaseSignature temp : signResult.getSucceeded())
    {
        System.out.print(temp.getSignatureType()+" at page #"+temp.getPageNumber()+": Id:"+temp.getSignatureId()+".");
    }
}

Implement Searching in the Certificate documents

🌐 In this release, we’ve introduced new search options CertificateSearchOptions to provide search capabilities for certificates. Now, users can quickly locate critical information within their certificates by searching for particular data. Example below demonstrates how to search if certificate contain specific string from the serial number.

/// <summary>
/// Search if certificate contain specific string from the serial number.
/// </summary>
Signature signature = new Signature(certificatePath, loadOptions);
{
    CertificateSearchOptions options = new CertificateSearchOptions();
    {
        // check the serial number
        options.setText("AAD0D15C628A");
        // find exact math
        options.setMatchType(TextMatchType.Contains);
    };

    // search for certificate data
    List<MetadataSignature> result = signature.search(MetadataSignature.class, options);
    if (result.size()>0)
    {
        System.out.print("\nCertificate contains following search results");
        for (MetadataSignature temp : result)
        {
            System.out.print("\t\t-"+temp.getName()+" - "+temp.getValue());
        }
    }
    else
    {
        System.out.print("\nCertificate failed search process.");
    }
}

Extend DocumentResultSignature with the Error message

🌐 For the archive document processing, when the SignResult contains list of succeeded and failed DocumentResultSignature elements we added new property to monitor the error. Following example shows how to analyze this variable.

/// <summary>
/// Support Succeeded and Failed list as result of processing archives
/// </summary>
Signature signature = new Signature("sample.zip");
{
    // create sign options
    TextSignOptions options = new TextSignOptions("signed!")
    {
        // set signature position
        options.setLeft(100);
        options.setTop(100);
    };
    // sign archive to new zip file
    SignResult result = signature.sign("output.zip", options);
    // analyze the failed documents
    for (BaseSignature o : result.getFailed())
    {
        DocumentResultSignature document = (DocumentResultSignature)o;
        System.out.print("ERROR in Document #"+number++ +"-"+document.getFileName()+": "+document.getErrorMessage());
    }
}

Implement verification DICOM image documents for signatures

🌐 The verification process within the DICOM documents does not look different than verification for single-page raster image, except for the fact that all result images will contain the page number of the layer where the signature was verified.

/// <summary>
/// Verify multi-layer image document for signatures.
/// This example provided for formats like DICOM
/// </summary>
Signature signature = new Signature("signed.dcm");
{
   QrCodeVerifyOptions options = new QrCodeVerifyOptions();
    {
        options.setAllPages(true);
        options.setText("Patient #36363393");
        options.setMatchType(TextMatchType.Contains);
    };

    VerificationResult result = signature.verify(options);
    if (result.isValid())
    {
        System.out.print("\nDICOM "+filePath+" has "+result.getSucceeded().size()+" successfully verified signatures!");
    }
    else
    {
        System.out.print("\nDocument "+filePath+" failed verification process.");
    }
}

Implement preview for DICOM image documents

🌐 How to preview the DICOM image information

/// <summary>
/// Generate preview for DICOM document 
/// </summary>
Signature signature = new Signature("signed.dcm"))
{
    PreviewOptions previewOption = new PreviewOptions(new PageStreamFactory() {
        @Override
        public OutputStream createPageStream(int pageNumber) {
            return generatePageStream(pageNumber);
        }

        @Override
        public void closePageStream(int pageNumber, OutputStream pageStream) {
            releasePageStream(pageNumber, pageStream);
            }
    });

    signature.generatePreview(previewOption);
    System.out.print("\nDICOM ['"+filePath+"'] pages previews were successfully generated!");
}

private static OutputStream generatePageStream(int pageNumber)
{
    try {
    String filePath = Constants.OutputPath +"\\SignDicomImageAdvanced\\image-"+pageNumber+".jpg";
        return new FileOutputStream(filePath);
    }catch (Exception e){
        throw new RuntimeException(e.getMessage());
    }
}

private static void releasePageStream(int pageNumber, OutputStream pageStream)
{
    try {
        pageStream.close();
        String imageFilePath = new File(Constants.OutputPath + "\\SignDicomImageAdvanced", "image-" +pageNumber +  ".jpg").getPath();
        System.out.print("Image file "+imageFilePath+" is ready for preview");
    }catch (Exception e){
        System.out.print(e.getMessage());
    }
}

Respect saving XmpData package for DICOM

🌐 In this release, we’ve introduced extended DICOM XmpData DicomXmpType to provide ability to get and update DICOM metadata. It may be done using DicomSaveOptions

/// <summary>
/// Sign multi-layer image document and add xmp metadata
/// </summary>
Signature signature = new Signature("sample.dcm"))
{
    QrCodeSignOptions options = new QrCodeSignOptions("Patient #36363393. R: No-Issues");
    {
        // set QR area
        options.setWidth(100);
        options.setHeight(100);
        // put right bottom corner
        options.setVerticalAlignment(VerticalAlignment.Bottom);
        options.setHorizontalAlignment(HorizontalAlignment.Right);
        Padding padding = new  Padding();
        padding.setRight(5);
        padding.setLeft(5);
        options.setMargin(padding);
    };

    DicomSaveOptions dicomSaveOptions = new DicomSaveOptions()
    List<DicomXmpEntry> list = new ArrayList<DicomXmpEntry>();
    list.add(new DicomXmpEntry(DicomXmpType.PatientName, "Patient #4"));
    dicomSaveOptions.setXmpEntries(list);

    SignResult signResult = signature.sign(outputFilePath, options, dicomSaveOptions);
}

Implement gathering extended DICOM XmpData into the DocumentInfo

/// <summary>
/// Gathering extended DICOM XmpData into the DocumentInfo
/// </summary>
Signature signature = new Signature("sample.dcm");
{
    System.out.print("\nList of DICOM xmp metadata:");
    IDocumentInfo signedDocumentInfo = signature.getDocumentInfo();
    for (MetadataSignature item : signedDocumentInfo.getMetadataSignatures())
    {
        System.out.print(item.toString());
    }
}