如何使用iText将签名附加到pdf?

时间:2019-06-18 13:46:35

标签: java jquery pdf itext

我在实现中适用于简单情况,但不适用于复杂情况。原始pdf显示在中央窗格中。我有一个侧窗格,其中有一些矩形,例如“名称”,“签名”,“时间戳”,可以将其拖到pdf上。使用jQuery可拖动和可拖放,我能够捕获放置点的坐标并将其存储在数据库中。使用iText的PdfStamper,我得到PdfContentByte,并向其中添加签名图像。如果pdf文档是同类文档,则可以正常工作-假设所有页面均为Letter尺寸。但是如果页面是横向和纵向混合使用,则失败。签名被嵌入在横向页面的适当位置,但没有嵌入纵向页面的适当位置。如果所有页面都是横向的,那么就没有问题。同样,如果所有页面都是纵向的,那么也没问题。

  

我了解图像以像素为单位,而pdf尺寸   以点。所以我已经将像素的图像坐标转换为   点(0.75)。还考虑到图像原点位于左上方
  角,而在pdf中,原点在左下角,对于图像y轴是   向南,但对于pdf,y轴是向北。

如何处理?

编辑:

  

这里是代码:DocumentField是一个POJO,它具有签名坐标的属性

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

第二次修改

此第二个示例pdf有3页,尺寸分别为:第一页682.04 x 297.12,第二页610.52 x 788.6,第三页与第二页611 X 789.08几乎相同。首先,我尝试将签名放置在每页的左上角。然后我尝试将签名放置在每页的左下角,但没有成功,我将图像的坐标存储到数据库中,值分别为(0,350.484),(0,352.328)和(7, 301.688)。第三个值的x坐标应为0,我们可以忽略此小偏差。现在对于第一页,签名被嵌入到正确的位置。但是对于第二和第三页,它们几乎是页面在y方向上的中间位置,x可以确定,即0。第1页的图像的坐标为(0.0,226.21698),第2页的图像的坐标为(89.04 ,524.354),第3页的图像坐标为(94.29,562.814)。

第3次修改 捕获可放置签名小部件的放置点坐标的jQuery代码如下:

 $(".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;
      }
 });

1 个答案:

答案 0 :(得分:1)

在第一个示例文件上绘制图像

我试图重现这样的问题:

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测试testStampImagesLikeSubhenduMahanta

使用此POJO类

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助手类)

在评论中您说:

  

我要在所有5页中的同一位置签名。

因此,我为所有70实例使用了 left 600 top DocumentField

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));

但是结果看起来像这样:

screenshot

如您所见,签名图像是人们期望的位置。

因此,我无法复制您的问题。

您应该检查所有DocumentField实例的值并检查其正确性。

分析第二个示例文件的数据库值

  

此第二个示例pdf有3页,尺寸分别为:第一页682.04 x 297.12,第二页610.52 x 788.6,第三页与第二页611 X 789.08几乎相同。 [...]

     

然后我尝试将签名放置在每页的左下角,但没有成功。我正在存储拖动到数据库的图像坐标,值分别为(0,350.484),(0,352.328)和(7,301.688)。 [...]

     

现在,在第一页中,签名被嵌入到正确的位置。但是对于第二页和第三页,它们几乎是y方向上的页面中间

请考虑到第二页和第三页是第一页的两倍多,并在所有页上使用大致相同的数据库y(以像素为单位,从可见页的顶部开始,以像素为单位的字段的y)值。 350,第2页:约350,第3页:约300)可能具有完全不同的视觉效果:这些数据库值显然会将签名放在第二页和第三页的中间高度。

因此,数据库中的值根本没有任何意义。请检查生成和存储这些坐标的过程。