我们如何获取签名属性详细信息并将其作为PDFBox 2.0.12中visibleSignature的一部分

时间:2019-02-07 07:18:21

标签: digital-signature pdfbox

以下是作为可见签名一部分的签名详细信息

我正在使用Apache PDFBox 2.0.12签署PDF文档。我也想启用视觉签名。

我能够签名并创建视觉签名。问题在于我们如何获得有关签名属性的详细信息(例如,证书本身的SignedBy,SignedDate,并在文本中提到,如here所述:

public class VisibleSignature2 extends SignatureBase { 
    private SignatureOptions signatureOptions;
    private VisibleSignatureOptions visibleSignatureOptions;

    public VisibleSignature2(PrivateKey keystore, List<Certificate> pin, VisibleSignatureOptions visibleSignatureOptions) {
        super(keystore, pin);
        this.visibleSignatureOptions = visibleSignatureOptions;
    }

    public void signPDF(InputStream inputFile, OutputStream signedFile, Rectangle2D humanRect) throws IOException {

        PDDocument doc = PDDocument.load(inputFile);
        int accessPermissions = SigUtils.getMDPPermission(doc);
        if (accessPermissions == 1) {
            throw new IllegalStateException("No changes to the document are permitted due to DocMDP transform parameters dictionary");
        }

        PDSignature signature = new PDSignature();
        PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
        PDRectangle rect = createSignatureRectangle(doc, humanRect);

        if (doc.getVersion() >= 1.5f && accessPermissions == 0) {
            SigUtils.setMDPPermission(doc, signature, 2);
        }

        if (acroForm != null && acroForm.getNeedAppearances()) {
            // PDFBOX-3738 NeedAppearances true results in visible signature becoming invisible
            // with Adobe Reader
            if (acroForm.getFields().isEmpty()) {
                // we can safely delete it if there are no fields
                acroForm.getCOSObject().removeItem(COSName.NEED_APPEARANCES);
                // note that if you've set MDP permissions, the removal of this item
                // may result in Adobe Reader claiming that the document has been changed.
                // and/or that field content won't be displayed properly.
                // ==> decide what you prefer and adjust your code accordingly.
            }
        }

        // default filter
        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
        signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);

        // the signing date, needed for valid signature
        signature.setSignDate(Calendar.getInstance());
        signatureOptions = new SignatureOptions();
        System.out.println("Visible Signature Reason is " + visibleSignatureOptions.hasVisibleSignatureReason());
        if (visibleSignatureOptions.isShouldSignatureBeVisible()) {
            System.out.println("Inside VisualSignature");
            signature.setReason(visibleSignatureOptions.getVisibleSignatureReason());
            signatureOptions.setVisualSignature(createVisualSignatureTemplate(doc, 0, rect));
        }
        signatureOptions.setPage(0);
        doc.addSignature(signature, this, signatureOptions);
        // write incremental (only for signing purpose)
        doc.saveIncremental(signedFile);
        doc.close();
        IOUtils.closeQuietly(signatureOptions);
    }

    private PDRectangle createSignatureRectangle(PDDocument doc, Rectangle2D humanRect) {
        float x = (float) humanRect.getX();
        float y = (float) humanRect.getY();
        float width = (float) humanRect.getWidth();
        float height = (float) humanRect.getHeight();
        PDPage page = doc.getPage(0);
        PDRectangle pageRect = page.getCropBox();
        PDRectangle rect = new PDRectangle();
        // signing should be at the same position regardless of page rotation.
        switch (page.getRotation()) {
            case 90:
                rect.setLowerLeftY(x);
                rect.setUpperRightY(x + width);
                rect.setLowerLeftX(y);
                rect.setUpperRightX(y + height);
                break;
            case 180:
                rect.setUpperRightX(pageRect.getWidth() - x);
                rect.setLowerLeftX(pageRect.getWidth() - x - width);
                rect.setLowerLeftY(y);
                rect.setUpperRightY(y + height);
                break;
            case 270:
                rect.setLowerLeftY(pageRect.getHeight() - x - width);
                rect.setUpperRightY(pageRect.getHeight() - x);
                rect.setLowerLeftX(pageRect.getWidth() - y - height);
                rect.setUpperRightX(pageRect.getWidth() - y);
                break;
            case 0:
            default:
                rect.setLowerLeftX(x);
                rect.setUpperRightX(x + width);
                rect.setLowerLeftY(pageRect.getHeight() - y - height);
                rect.setUpperRightY(pageRect.getHeight() - y);
                break;
        }
        return rect;
    }

    private InputStream createVisualSignatureTemplate(PDDocument srcDoc, int pageNum, PDRectangle rect) throws IOException {
        PDDocument doc = new PDDocument();

        PDPage page = new PDPage(srcDoc.getPage(pageNum).getMediaBox());
        doc.addPage(page);
        PDAcroForm acroForm = new PDAcroForm(doc);
        doc.getDocumentCatalog().setAcroForm(acroForm);
        PDSignatureField signatureField = new PDSignatureField(acroForm);
        PDAnnotationWidget widget = signatureField.getWidgets().get(0);
        List<PDField> acroFormFields = acroForm.getFields();
        acroForm.setSignaturesExist(true);
        acroForm.setAppendOnly(true);
        acroForm.getCOSObject().setDirect(true);
        acroFormFields.add(signatureField);

        widget.setRectangle(rect);

        // from PDVisualSigBuilder.createHolderForm()
        PDStream stream = new PDStream(doc);
        PDFormXObject form = new PDFormXObject(stream);
        PDResources res = new PDResources();
        form.setResources(res);
        form.setFormType(1);
        PDRectangle bbox = new PDRectangle(rect.getWidth(), rect.getHeight());
        float height = bbox.getHeight();
        Matrix initialScale = null;
        switch (srcDoc.getPage(pageNum).getRotation()) {
            case 90:
                form.setMatrix(AffineTransform.getQuadrantRotateInstance(1));
                initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
                height = bbox.getWidth();
                break;
            case 180:
                form.setMatrix(AffineTransform.getQuadrantRotateInstance(2));
                break;
            case 270:
                form.setMatrix(AffineTransform.getQuadrantRotateInstance(3));
                initialScale = Matrix.getScaleInstance(bbox.getWidth() / bbox.getHeight(), bbox.getHeight() / bbox.getWidth());
                height = bbox.getWidth();
                break;
            case 0:
            default:
                break;
        }
        form.setBBox(bbox);

        PDFont font = PDType1Font.HELVETICA_BOLD;
        // from PDVisualSigBuilder.createAppearanceDictionary()
        PDAppearanceDictionary appearance = new PDAppearanceDictionary();
        appearance.getCOSObject().setDirect(true);
        PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
        appearance.setNormalAppearance(appearanceStream);
        widget.setAppearance(appearance);

        PDPageContentStream cs = new PDPageContentStream(doc, appearanceStream);


        if (initialScale != null) {
            cs.transform(initialScale);
        }

        // show text
        float fontSize = 10;
        float leading = fontSize * 1.5f;
        cs.beginText();
        cs.setFont(font, fontSize);
        cs.setNonStrokingColor(Color.black);
        cs.newLineAtOffset(fontSize, height - leading);
        cs.setLeading(leading);
        cs.showText("(Signature very wide line 1)");
        cs.newLine();
        cs.showText("(Signature very wide line 2)");
        cs.newLine();
        cs.showText("(Signature very wide line 3)");
        cs.endText();

        cs.close();

        cs.close();
        // no need to set annotations and /P entry
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        doc.save(baos);
        doc.close();
        return new ByteArrayInputStream(baos.toByteArray());
    }

}

0 个答案:

没有答案