vanduc1102
vanduc1102

Reputation: 6245

pdfbox - sign landscape file error

I am using pdfbox-1.8.8 to do the signing function on PDF file.

It works well with PDF file in portrait mode. But with landscape file, I have an issue

Position of signatory I want to sign

Result of the sign

It looks like the coordinate is wrong for the landscape file.

Does anyone know what is wrong with the file ?

Here is the link of pdf file

Here is the code I used to sign

public void signDetached(String inputFilePath, String outputFilePath, String signatureImagePath, Sign signProperties) {
    OutputStream outputStream = null;
    InputStream inputStream = null;
    PDDocument document = null;
    InputStream signImageStream = null;

    try {
        setTsaClient(null);
        document = PDDocument.load(inputFilePath);
        // create signature dictionary
        PDSignature signature = new PDSignature();
        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
        signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
        signature.setName("VANDUC1102");
        signature.setLocation(null);
        String displayName = "Hello World, Document signed by VANDUC1102";
        String reason = reasonText+ " " + displayName;
        signature.setReason(reason);

        // the signing date, needed for valid signature
        signature.setSignDate(Calendar.getInstance());            
        int signatureInPage = signProperties.getPageNumber() + 1;
        signImageStream = new FileInputStream(new File(signatureImagePath));
        PDVisibleSignDesigner visibleSig = new PDVisibleSignDesigner(inputFilePath, signImageStream, signatureInPage);

        float xAxis = convertPixel2Point(signProperties.getX()) ;
        float yAxis = convertPixel2Point(signProperties.getY());               
        float signImageHeight = convertPixel2Point(signImageHeight);    
        float signImageWidth = convertPixel2Point(signImageWidth);

        visibleSig.xAxis(xAxis)
                .yAxis(yAxis)
                .zoom(0)
                .signatureFieldName("Signature")
                .height(signImageHeight)
                .width(signImageWidth);
        PDVisibleSigProperties signatureProperties = new PDVisibleSigProperties();

        signatureProperties.signerName(eiUser.getName())
                 .signerLocation(null)
                 .signatureReason(reason)
                 .preferredSize(0)
                 .page(signProperties.getPageNumber())
                 .visualSignEnabled(true)
                 .setPdVisibleSignature(visibleSig)
                 .buildSignature();
         // register signature dictionary and sign interface
        SignatureOptions signatureOptions = new SignatureOptions();
        signatureOptions.setVisualSignature(signatureProperties);
        signatureOptions.setPage(signatureInPage);
        document.addSignature(signature, this, signatureOptions);

        File outputFile = new File(outputFilePath);
        outputStream = new FileOutputStream(outputFile);
        inputStream = new FileInputStream(inputFilePath);
        IOUtils.copyStream(inputStream, outputStream);
        document.saveIncremental(inputStream, outputStream);
        outputStream.flush();
    } catch (COSVisitorException | SignatureException | IOException ex) {
        log.error("signDetached ", ex);
    } finally {
        IOUtils.closeStream(outputStream);
        IOUtils.closeStream(inputStream);
        IOUtils.closeStream(signImageStream);
        IOUtils.closeStream(document);
    }
}
private float convertPixel2Point(float pixel){
    return pixel * (float) 72/96;
}

As I said this code work well with portrait PDF

Thanks.

Upvotes: 2

Views: 1952

Answers (2)

Tilman Hausherr
Tilman Hausherr

Reputation: 18851

Starting with version 2.0.5, adjusting the signature field for rotated pages will be possible with the adjustForRotation() call. In the CreateVisibleSignature.java example, this line

visibleSignDesigner.xAxis(x).yAxis(y).zoom(zoomPercent);

has to be changed to this:

visibleSignDesigner.xAxis(x).yAxis(y).zoom(zoomPercent).adjustForRotation();

The change (issue PDFBOX-3671) is partially based on the answer by @mkl. It will not be available for 1.8.*.

Update 24.10.2018:

All 2.0.* versions up to 2.0.12 had the problem that adjustForRotation() was hard-coded for 200 x 100 image size. This has been fixed in 2.0.13. Alternatively derive your own PDVisibleSignDesigner and extend adjustForRotation() with the correct implementation:

public PDVisibleSignDesigner adjustForRotation()
{
    switch (rotation)
    {
        case 90:
            float temp = yAxis;
            yAxis = pageHeight - xAxis - imageWidth;
            xAxis = temp;

            affineTransform = new AffineTransform(
                    0, imageHeight / imageWidth, -imageWidth / imageHeight, 0, imageWidth, 0);

            temp = imageHeight;
            imageHeight = imageWidth;
            imageWidth = temp;
            break;

        case 180:
            float newX = pageWidth - xAxis - imageWidth;
            float newY = pageHeight - yAxis - imageHeight;
            xAxis = newX;
            yAxis = newY;

            affineTransform = new AffineTransform(-1, 0, 0, -1, imageWidth, imageHeight);
            break;

        case 270:
            temp = xAxis;
            xAxis = pageWidth - yAxis - imageHeight;
            yAxis = temp;

            affineTransform = new AffineTransform(
                    0, -imageHeight / imageWidth, imageWidth / imageHeight, 0, 0, imageHeight);

            temp = imageHeight;
            imageHeight = imageWidth;
            imageWidth = temp;
            break;

        case 0:
        default:
            break;
    }
    return this;
}

Upvotes: 4

mkl
mkl

Reputation: 95898

The page in question has a non-zero Rotate value. The PDFBox visual signing classes completely ignore this value, so one has to give it the coordinates and dimensions as if the page was not rotated.

This can be done by adding the following switch statement:

float xAxis = convertPixel2Point(/*signProperties.getX()*/x) ;
float yAxis = convertPixel2Point(/*signProperties.getY()*/y);               
float signImageHeight = convertPixel2Point(/*signImageHeight*/324);    
float signImageWidth = convertPixel2Point(/*signImageWidth*/309);

int rotation = getPageRotation(inputFilePath, page) % 360;
switch (rotation)
{
case 0:
    // all ok;
    break;
case 90:
    visibleSig.affineTransformParams(new byte[] {0, 1, -2, 0, 100, 0})
              .formaterRectangleParams(new byte[]{0, 0, 100, 100});

    float temp = yAxis;
    yAxis = visibleSig.getPageHeight() - xAxis - signImageWidth;
    xAxis = temp;

    temp = signImageHeight;
    signImageHeight = signImageWidth;
    signImageWidth = temp;

    break;
case 180:
    // Implement in a similar fashion
case 270:
    // Implement in a similar fashion
}

visibleSig.xAxis(xAxis)
          .yAxis(yAxis)
          .zoom(0)
          .signatureFieldName("Signature")
          .height(signImageHeight)
          .width(signImageWidth);

and the following method:

private int getPageRotation(String documentPath, int page) throws IOException
{
    try (PDDocument document = PDDocument.load(documentPath))
    {
        List<?> pages = document.getDocumentCatalog().getAllPages();
        PDPage pageObject =(PDPage) pages.get(page);
        return pageObject.getRotation();
    }
}

For Rotate values of 180 and 270, analogous corrections have to be made.

(Test methods testLandscapeOriginal and testLandscapeFixed in SignLikeVanduc1102)

Upvotes: 4

Related Questions