简体   繁体   中英

How to attach signatures to pdf using iText?

I have in implementation which works for simple cases but not for complicated cases. The original pdf gets displayed in a central pane.I have a side pane in which there are some rectangles like "Name", "Signature", "Timestamp" which can be dragged on the pdf. Using jQuery draggable & droppable I am able to capture co-ordinates of the drop points & store the same in database. Using PdfStamper of iText I get PdfContentByte & I add the signature image to it. It works if the pdf document is homogeneous - say all the pages are of Letter size. But fails in cases where pages are a mixture of landscape & portrait. The signature gets embedded at proper place for landscape pages but not at proper place for portrait pages. If all the pages are landscape then there is no problem. Similarly if all the pages are portrait then also no problem.

I understand that images are measured in pixels whereas pdf dimensions are in points. So I have converted image co-ordinates in pixel to point (0.75). Also taken in account that for images origin is at top left
corner, whereas in pdf origin is at bottom left corner, for image y axis is south-wards, but for pdf y axis is north-wards.

How to go about it?

Edit:

Here is the code: DocumentField is a POJO which has properties for signature coordinates

public void writeDocumentFields(List<DocumentField> documentField,File file, File outputFile) throws IOException    {
    try {
        PdfReader pdfReader = new PdfReader(file.getAbsolutePath());
        PdfReader.unethicalreading=true;
        PdfStamper pdfStamper = new PdfStamper(pdfReader,new FileOutputStream(outputFile));
        for(DocumentField df:documentField){
            int pageNumber = df.getPageNumber()+1;
            PdfContentByte content = pdfStamper.getOverContent(pageNumber);
            Rectangle cropBox = pdfReader.getCropBox(pageNumber);
            if(pdfReader.getPageRotation(pageNumber) > 0) {
                float width = cropBox.getRight();
                cropBox.setRight(cropBox.getHeight());
                cropBox.setTop(width);                   
            }

            if(df.getFieldType().equals("image")){
                df.setxPosition(
                    Float.parseFloat(df.getLeft())*
                    CONVERSION_FACTOR_FROM_PIXEL_TO_POINT);
                df.setyPosition(Float.parseFloat(df.getTop())*CONVERSION_FACTOR_FROM_PIXEL_TO_POINT);
                float x = cropBox.getLeft()  + df.getxPosition();
                float y = cropBox.getTop()  - df.getyPosition();
                Image image = Image.getInstance(df.getFieldValue());
                image.scaleToFit(150*CONVERSION_FACTOR_FROM_PIXEL_TO_POINT, 50*CONVERSION_FACTOR_FROM_PIXEL_TO_POINT);
                image.setAbsolutePosition(x, y - 36f);
                content.addImage(image);
            }else if(df.getFieldType().equals("checkbox")){
                //...
            }else{
                //...
            }
        }
        pdfStamper.close();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (DocumentException e) {
        e.printStackTrace();
    }
}   

PDF with both Landscape & Portrait, I am always signing at same place

Another Example mixed pdf

2nd edit

This 2nd example pdf has 3 pages, dimensions are for 1st page 682.04 x 297.12, 2nd page 610.52 x 788.6, 3rd page is almost same as 2nd page 611 X 789.08. First I tried to place the signature at the top left corner of each page.This succeeded. Then I tried to place the signature at the bottom left corner of each page.This did not succeed.I am storing the co-ordinates of image dragged to database & the values are (0, 350.484),(0,352.328) and (7, 301.688). x coordinate of 3rd value should have been 0, we may ignore this small deviation. Now for 1st page the signature gets embedded at right place. But for 2nd & 3rd page they are almost the middle of the page in y direction, x is ok ie 0. coordinates of image for page no: 1 are (0.0,226.21698), coordinates of image for page no: 2 are (89.04,524.354), coordinates of image for page no: 3 are (94.29,562.814).

3rd Edit The jQuery code which captures the co-ordinates of drop point of droppable signature widget is like this:

 $(".drop").droppable({
      accept: '.dragSigners',
      activeClass: "drop-area",

      drop: function(e, ui) {
        var off = $(this).offset();
        leftPosition  = ui.offset.left - off.left;
        topPosition   = ui.offset.top - off.top;
      }
 });

Drawing images on your first example file

I tried to reproduce the issue like this:

float CONVERSION_FACTOR_FROM_PIXEL_TO_POINT = 0.75f;
List<DocumentField> documentField = new ArrayList<>();

try (   InputStream resource = getClass().getResourceAsStream("Mix PDF.pdf");
        InputStream imageResource = getClass().getResourceAsStream("Signature.png") ) {
    byte[] imageBytes = StreamUtil.inputStreamToArray(imageResource);
    documentField.add(new DocumentField(0, "70", "600", "image", imageBytes));
    documentField.add(new DocumentField(1, "70", "600", "image", imageBytes));
    documentField.add(new DocumentField(2, "70", "600", "image", imageBytes));
    documentField.add(new DocumentField(3, "70", "600", "image", imageBytes));
    documentField.add(new DocumentField(4, "70", "600", "image", imageBytes));

    PdfReader pdfReader = new PdfReader(resource);
    PdfReader.unethicalreading=true;
    PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileOutputStream(new File(RESULT_FOLDER, "StampImagesLikeSubhenduMahanta.pdf")));
    for(DocumentField df:documentField){
        int pageNumber = df.getPageNumber()+1;
        PdfContentByte content = pdfStamper.getOverContent(pageNumber);
        Rectangle cropBox = pdfReader.getCropBox(pageNumber);
        if(pdfReader.getPageRotation(pageNumber) > 0) {
            float width = cropBox.getRight();
            cropBox.setRight(cropBox.getHeight());
            cropBox.setTop(width);                   
        }

        if(df.getFieldType().equals("image")){
            df.setxPosition(
                Float.parseFloat(df.getLeft())*
                CONVERSION_FACTOR_FROM_PIXEL_TO_POINT);
            df.setyPosition(Float.parseFloat(df.getTop())*CONVERSION_FACTOR_FROM_PIXEL_TO_POINT);
            float x = cropBox.getLeft()  + df.getxPosition();
            float y = cropBox.getTop()  - df.getyPosition();
            Image image = Image.getInstance(df.getFieldValue());
            image.scaleToFit(150*CONVERSION_FACTOR_FROM_PIXEL_TO_POINT, 50*CONVERSION_FACTOR_FROM_PIXEL_TO_POINT);
            image.setAbsolutePosition(x, y - 36f);
            content.addImage(image);
        }else if(df.getFieldType().equals("checkbox")){
            //...
        }else{
            //...
        }
    }
    pdfStamper.close(); 
}

( StampImages test testStampImagesLikeSubhenduMahanta )

with this POJO class

class DocumentField {
    DocumentField(int pageNumber, String left, String top, String fieldType, byte[] fieldValue) {
        this.pageNumber = pageNumber;
        this.left = left;
        this.top = top;
        this.fieldType = fieldType;
        this.fieldValue = fieldValue;
    }

    int getPageNumber()                 {   return pageNumber;          }
    final int pageNumber;
    String getLeft()                    {   return left;                }
    final String left;
    String getTop()                     {   return top;                 }
    final String top;
    String getFieldType()               {   return fieldType;           }
    final String fieldType;
    byte[] getFieldValue()              {   return fieldValue;          }
    final byte[] fieldValue;
    float getxPosition()                {   return xPosition;           }
    void setxPosition(float xPosition)  {   this.xPosition = xPosition; }
    float xPosition = 0;
    float getyPosition()                {   return yPosition;           }
    void setyPosition(float yPosition)  {   this.yPosition = yPosition; }
    float yPosition = 0;
}

( StampImages helper class)

In a comment you said:

I am signing at same place in all 5 pages.

Thus, I used left values 70 and top values 600 for all DocumentField instances

documentField.add(new DocumentField(0, "70", "600", "image", imageBytes));
documentField.add(new DocumentField(1, "70", "600", "image", imageBytes));
documentField.add(new DocumentField(2, "70", "600", "image", imageBytes));
documentField.add(new DocumentField(3, "70", "600", "image", imageBytes));
documentField.add(new DocumentField(4, "70", "600", "image", imageBytes));

but the result looks like this:

截图

As you see, the signature images are where one would expect them to be.

Thus, I cannot reproduce your issue.

You should inspect the values of all DocumentField instances and check their correctness.

Analyzing the database values for your second example file

This 2nd example pdf has 3 pages, dimensions are for 1st page 682.04 x 297.12, 2nd page 610.52 x 788.6, 3rd page is almost same as 2nd page 611 X 789.08. [...]

Then I tried to place the signature at the bottom left corner of each page.This did not succeed.I am storing the co-ordinates of image dragged to database & the values are (0, 350.484),(0,352.328) and (7, 301.688). [...]

Now for 1st page the signature gets embedded at right place. But for 2nd & 3rd page they are almost the middle of the page in y direction

Considering that the second and third page are more than twice as high as the first one, using approximately the same database y (top y of field in pixel from top of visible page) value on all pages (page 1: ca. 350, page 2: ca. 350, page 3: ca. 300) is likely to have entirely different visual effects: Those database values obviously will put the signature at a middle height on the second and third page.

So the values in your database simply make no sense. Please check the process generating and storing those coordinates.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM