'Extract images of signatures contained in a pdf file with iText7

I am wondering how we can use ITEXT7 to extract image info associated to digital signatures. I know there have been similar questions asked in the past, but they were mostly around ITEXT5, which is quite different from the ITEXT7 after all the updates and modifications to the software.



Solution 1:[1]

You can extract the image from a signature appearance using low-level API. Complete Java code:

private void saveImageFromSignature(PdfDocument document, String fieldName) throws IOException {
    PdfAcroForm acroForm = PdfAcroForm.getAcroForm(document, false);
    PdfDictionary xObject = acroForm.getField(name)
                                    .getWidgets()
                                    .get(0)
                                    .getNormalAppearanceObject()
                                    .getAsDictionary(PdfName.Resources)
                                    .getAsDictionary(PdfName.XObject)
                                    .getAsStream(new PdfName("FRM"))
                                    .getAsDictionary(PdfName.Resources)
                                    .getAsDictionary(PdfName.XObject);
    PdfStream stream = xObject.getAsStream(new PdfName("Im1"));


    PdfImageXObject image      = new PdfImageXObject(stream);
    BufferedImage   result     = createImageFromBytes(image.getImageBytes());
    //pdf allows using masked image in the signature appearance 
    PdfStream       maskStream = (PdfStream) stream.getAsStream(PdfName.SMask);
    if (maskStream != null) {
        PdfImageXObject maskImage = new PdfImageXObject(maskStream);

        BufferedImage maskBimage = createImageFromBytes(maskImage.getImageBytes());
        String fileMask = String.format(getOutputFolder() + "/file_mask_%d.%s",
                                        image.getPdfObject().getIndirectReference().getObjNumber(),
                                        image.identifyImageFileExtension());
        ImageIO.write(maskBimage,
                      image.identifyImageFileExtension(),
                      new File(fileMask));
        //the mask defines an alfa channel 
        Image transpImg = transformToTransperency(maskBimage);
        result = applyTransperency(result, transpImg);
       


    }
    String filenameComp = String.format(getOutputFolder() + "/file_comp_%d.%s",
                                        image.getPdfObject().getIndirectReference().getObjNumber(),
                                        image.identifyImageFileExtension());


    ImageIO.write(result,
                  image.identifyImageFileExtension(),
                  new File(filenameComp));
    document.close();
}
private Image transformToTransperency(BufferedImage bi) {
    ImageFilter filter = new RGBImageFilter() {
        @Override
        public int filterRGB(int x, int y, int rgb) {
            return (rgb << 8) & 0xFF000000;
        }
    };
    ImageProducer ip = new FilteredImageSource(bi.getSource(), filter);
    return Toolkit.getDefaultToolkit().createImage(ip);

}
private BufferedImage applyTransperency(BufferedImage bi, Image mask) {
    BufferedImage dest = new BufferedImage(
            bi.getWidth(), bi.getHeight(),
            BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2 = dest.createGraphics();
    g2.drawImage(bi, 0, 0, null);
    AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.DST_IN, 1.0F);
    g2.setComposite(ac);
    g2.drawImage(mask, 0, 0, null);
    g2.dispose();
    return dest;
}

Upd: This works for a very limited number of cases. Thanks for @mkl.

Solution 2:[2]

First of all, thank you for the proposals which personally guided me. After several tries, here is the code that worked for me:

public void extract(String inputFilename, String fieldName) throws IOException {

    try (PdfDocument document = new PdfDocument(new PdfReader(inputFilename))){

        PdfAcroForm acroForm = PdfAcroForm.getAcroForm(document, false);
        final PdfFormField signatorySignature1 = acroForm.getField(fieldName);

        final PdfDictionary appearanceDic = signatorySignature1.getPdfObject().getAsDictionary(PdfName.AP);
        final PdfStream normalAppearance = appearanceDic.getAsStream(PdfName.N);
        final PdfDictionary ressourceDic = normalAppearance.getAsDictionary(PdfName.Resources);
        PdfResources resources = new PdfResources(ressourceDic);

        final ImageRenderInfo imageRenderInfo = extractImageRenderInfo(normalAppearance.getBytes(), resources);

        Files.write(
                Path.of(inputFilename + "_" + fieldName + "_" + System.currentTimeMillis() + ".png"),
                imageRenderInfo.getImage().getImageBytes());

    } catch (Exception e) {
        e.printStackTrace();
    }

}

public ImageRenderInfo extractImageRenderInfo(byte[] contentBytes, PdfResources pdfResource) {
    MyLocationExtractionStrategy strategy = new MyLocationExtractionStrategy();
    PdfCanvasProcessor parser = new PdfCanvasProcessor(strategy, new HashMap<>());
    parser.processContent(contentBytes, pdfResource);
    return strategy.getImageRenderInfo();
}

class MyLocationExtractionStrategy implements ILocationExtractionStrategy {

    private ImageRenderInfo imageRenderInfo;

    @Override public Collection<IPdfTextLocation> getResultantLocations() {
        return null;
    }

    @Override public void eventOccurred(IEventData iEventData, EventType eventType) {

        if (eventType.equals(EventType.RENDER_IMAGE)) {
            imageRenderInfo = (ImageRenderInfo)iEventData;
        }
    }

    @Override public Set<EventType> getSupportedEvents() {
        return null;
    }

    public ImageRenderInfo getImageRenderInfo() {
        return this.imageRenderInfo;
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Michel Valour