使用 pdfbox 1.8.8 进行视觉签名

2024-05-01

我正在尝试生成带有视觉签名和 pdfbox 的 PDF。我有两个流,似乎 pdfbox 只能处理文件。如果没有三个临时文件,我就无法使其工作。我可以看到从here https://github.com/apache/pdfbox/blob/b789c91d57f4a3b8763b35779309ea422db66c5d/examples/src/main/java/org/apache/pdfbox/examples/signature/CreateSignature.java该 API 已更改,但它仍然处理文件。

public void signPdf(InputStream originalPdf, OutputStream signedPdf,
        InputStream image, float x, float y,
        String name, String location, String reason) {

    File temp = null;
    File temp2 = null;
    File scratchFile = null;
    RandomAccessFile randomAccessFile = null;
    OutputStream tempOut = null;
    InputStream tempIn = null;
    try {
        /* Copy original to temporary file */
        temp = File.createTempFile("signed1", ".tmp");
        tempOut = new FileOutputStream(temp);
        copyStream(originalPdf, tempOut);
        tempOut.close();

        /* Read temporary file to second temporary file and stream */
        tempIn = new FileInputStream(temp);
        temp2 = File.createTempFile("signed2", ".tmp");
        tempOut = new FileOutputStream(temp2);
        copyStream(tempIn, tempOut);
        tempIn.close();
        tempIn = new FileInputStream(temp2);

        scratchFile = File.createTempFile("signed3", ".bin");
        randomAccessFile = new RandomAccessFile(scratchFile, "rw");

        /* Read temporary file */
        PDDocument document = PDDocument.load(temp, randomAccessFile);
        document.getCurrentAccessPermission().setCanModify(false);

        PDSignature signature = new PDSignature();
        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
        signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
        signature.setName(name);
        signature.setLocation(location);
        signature.setReason(reason);
        signature.setSignDate(Calendar.getInstance());

        PDVisibleSignDesigner signatureDesigner = new PDVisibleSignDesigner(
                document, image, document.getNumberOfPages());
        signatureDesigner.xAxis(250).yAxis(60).zoom(-90).signatureFieldName("signature");

        PDVisibleSigProperties signatureProperties = new PDVisibleSigProperties();
        signatureProperties.signerName(name).signerLocation(location)
                .signatureReason(reason).preferredSize(0).page(1)
                .visualSignEnabled(true).setPdVisibleSignature(signatureDesigner)
                .buildSignature();

        SignatureOptions options = new SignatureOptions();
        options.setVisualSignature(signatureProperties);

        document.addSignature(signature, dataSigner, options);

        /* Sign */
        document.saveIncremental(tempIn, tempOut);
        document.close();
        tempIn.close();

        /* Copy temporary file to an output stream */
        tempIn = new FileInputStream(temp2);
        copyStream(tempIn, signedPdf);
    } catch (IOException e) {
        logger.error("PDF signing failure", e);
    } catch (COSVisitorException e) {
        logger.error("PDF creation failure", e);
    } catch (SignatureException e) {
        logger.error("PDF signing failure", e);
    } finally {
        closeStream(originalPdf);
        closeStream(signedPdf);
        closeStream(randomAccessFile);
        closeStream(tempOut);
        deleteTempFile(temp);
        deleteTempFile(temp2);
        deleteTempFile(scratchFile);
    }
}

private void deleteTempFile(File tempFile) {
    if (tempFile != null && tempFile.exists() && !tempFile.delete()) {
        tempFile.deleteOnExit();
    }
}

private void closeStream(Closeable is) {
    if (is!= null) {
        try {
            is.close();
        } catch (IOException e) {
            logger.error("failure", e);
        }
    }
}

private void copyStream(InputStream is, OutputStream os) throws IOException {
    byte[] buffer = new byte[1024];
    int c;
    while ((c = is.read(buffer)) != -1) {
        os.write(buffer, 0, c);
    }
    is.close();
}

除了文件疯狂之外,我在签名上没有看到任何文字。结果如下:

当我使用 itext 库做类似的事情时,这就是它的样子

为什么视觉签名表示中缺少名称、位置和原因?我该如何解决这个问题?


为什么视觉签名表示中缺少名称、位置和原因?

它们不存在是因为它们没有被绘制。

iText 表示可视化签名的默认方式是将这些信息添加到可视化中。

PDFBox的默认方式'PDVisibleSigBuilder表示可视化签名没有这样的信息。

两者都没有错或对,两者都只是默认值。

毕竟,人们查找此类信息的规范位置是签名面板。

我该如何解决这个问题?

签名可视化的实际内容是由PDVisibleSigBuilder期间的实例signatureProperties.buildSignature():

public void buildSignature() throws IOException
{
    PDFTemplateBuilder builder = new PDVisibleSigBuilder();
    PDFTemplateCreator creator = new PDFTemplateCreator(builder);
    setVisibleSignature(creator.buildPDF(getPdVisibleSignature()));
}

因此,通过替换

    signatureProperties.signerName(name).signerLocation(location)
            .signatureReason(reason).preferredSize(0).page(1)
            .visualSignEnabled(true).setPdVisibleSignature(signatureDesigner)
            .buildSignature();

在你的代码中

    signatureProperties.signerName(name).signerLocation(location)
            .signatureReason(reason).preferredSize(0).page(1)
            .visualSignEnabled(true).setPdVisibleSignature(signatureDesigner);

    PDFTemplateBuilder builder = new ExtSigBuilder();
    PDFTemplateCreator creator = new PDFTemplateCreator(builder);
    signatureProperties.setVisibleSignature(creator.buildPDF(signatureProperties.getPdVisibleSignature()));

定制版本ExtSigBuilder这个的PDVisibleSigBuilder类,你可以在那里画任何你想要的东西,例如:

class ExtSigBuilder extends PDVisibleSigBuilder
{
    String fontName;

    public void createImageForm(PDResources imageFormResources, PDResources innerFormResource,
            PDStream imageFormStream, PDRectangle formrect, AffineTransform affineTransform, PDJpeg img)
            throws IOException
    {
        super.createImageForm(imageFormResources, innerFormResource, imageFormStream, formrect, affineTransform, img);

        PDFont font = PDType1Font.HELVETICA;
        fontName = getStructure().getImageForm().getResources().addFont(font);

        logger.info("Added font to image form: " + fontName);
    }

    public void injectAppearanceStreams(PDStream holderFormStream, PDStream innterFormStream, PDStream imageFormStream,
            String imageObjectName, String imageName, String innerFormName, PDVisibleSignDesigner properties)
            throws IOException
    {
        super.injectAppearanceStreams(holderFormStream, innterFormStream, imageFormStream, imageObjectName, imageName, innerFormName, properties);

        String imgFormComment = "q " + 100 + " 0 0 50 0 0 cm /" + imageName + " Do Q\n";
        String text = "BT /" + fontName + " 10 Tf (Hello) Tj ET\n";
        appendRawCommands(getStructure().getImageFormStream().createOutputStream(), imgFormComment + text);

        logger.info("Added text commands to image form: " + text);
    }
}

在图像表单(实际显示某些内容的表单)左下角上方以 10 号的 Helvetica 格式写下“Hello”。

PS:我认为这背后的面向对象结构应该彻底改革。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 pdfbox 1.8.8 进行视觉签名 的相关文章

随机推荐

  • 是否可以在水平面上制作 CSS3 列?

    我想知道是否可以水平生成 CSS3 列 例如 如果我有段落 a b c 和 d 并且我给它们 列数 2 则输出将是 a c b d 我想创建该列 以便输出变为 a b c d 有谁知道使用 css3 列时这是否可能 我希望我的页面是可扩展的
  • 超出最大调用堆栈大小 - Vue.js

    我有一个计算方法 可以让我计算产品的总价和折扣值 并希望获得以下值 总计 折扣 cartTotal var total 0 var discount Math round 0 1 this cartTotal 100 100 this ca
  • Android JSONObject内部多个JSONObject的解析

    我有一个来自服务器的 JSON 字符串 它看起来像这样 categories 0 term id 247 name Content Curation 1 term id 50 name Content Marketing 2 term id
  • Spring Boot 动态重置数据源

    当 Spring 配置文件或自定义数据库属性文件中的数据库名称 密码或主机名等数据库属性发生更改时 我尝试更新 Spring Boot 中的数据源 当属性更改时 应用程序必须通过侦听属性更改来自行更新 一旦数据库配置发生更改 我就使用 Sp
  • Laravel - 带有 join 和 concat 的查询生成器

    我试图从用户表中提取与 users groups 数据透视表中某个组匹配的所有用户 顺便说一句 我使用的是来自 Cartalyst 的 Sentry 2 这可以让所有用户的名字和姓氏连接起来 User select DB raw CONCA
  • 在 SQL Server 中增加一个整数

    菜鸟问题在这里 每次我更改 SQL Server 2008 R2 表中的某个记录时 我都想增加一条 RevisionId 记录 为此 我使用以下语法 UPDATE TheTable SET RevisionId SELECT Revisio
  • assessionid 名称更改

    如果我有一种简单的改变方法 我的生
  • 拉伸图像以填充浏览器窗口的宽度

    我有一个图像 我希望宽度能够填满浏览器窗口 无论窗口大小如何 如何在 HTML 和 CSS 中执行此操作 您可以添加宽度和高度为100 的div 也可以设置图像宽度和高度为100 div img src https picsum photo
  • 在 JavaScript 中使用科学计数法的陷阱

    这个问题是not寻求开发人员代码格式化意见 就我个人而言 我更喜欢在 JS 代码中使用科学计数法 因为我相信它更具可读性 为我 6e8比600000000 话虽这么说 我只是在寻找在 JS 中以科学记数法指定数字的潜在风险和缺点 我在野外并
  • playframework 全局设置 @Required 字段的自定义消息

    我正在寻求有关翻译 Play 框架 2 2 中的验证消息的帮助 我有必填字段 FE Required message To pole jest wymagane public String miesiac Required public S
  • 在 Gradle 中跳过禁用任务的依赖执行?

    是否有可能not当任务将被跳过时执行该任务的依赖项 在下面的例子中 我想jar 以及依赖项jar to not执行时如果服务器已经在运行则执行runServerTests 在这种情况下 服务器将由另一个进程启动 apply plugin j
  • PHP 未定义索引/未定义偏移解决方法[重复]

    这个问题在这里已经有答案了 可能的重复 PHP 注意 未定义的变量 和 注意 未定义的索引 https stackoverflow com questions 4261133 php notice undefined variable an
  • 经典的 asp/vbscript - 使用正则表达式修改所有 href

    在经典 ASP VB 脚本 中 我需要通过对当前 url 进行编码并在其前面挂起来修改字符串中包含的多个不同的 href 基本上 我想让所有的 href 都通过我的redirect asp 并将现有的 href 编码传递到新链接中 例如 现
  • JavaScript 日期对象 英国日期

    我有以下代码 datePicker change function dateSet datePicker val dateMinimum dateChange dateSetD new Date dateSet dateMinimumD n
  • 什么是克朗?我该如何使用这个? [关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 help reopen questions http
  • 如何扩展 gradle 的 clean 任务来删除文件?

    到目前为止 我已将以下内容添加到我的 build gradle 中 apply plugin base clean lt lt delete rootDir api library auto generated classes printl
  • 注册不起作用 - 服务器返回 404 错误代码

    MongoDB Stitch iOS SDK 注册问题 我试过这个 let stitchClient StitchClient appId
  • 在 php 8.1.0 上使用 phpunit 9.4 捕获警告、通知和弃用

    Quoting https phpunit readthedocs io en 9 5 writing tests for phpunit html testing php errors warnings and notices https
  • 如何使用 -fPIC 标志重新编译 libperl.a 目标文件?

    当试图修复一些问题时出现了这个问题安装问题 https stackoverflow com q 43191675 2173773 with QtCore4 https metacpan org pod QtCore4 在某一点make尝试运
  • 使用 pdfbox 1.8.8 进行视觉签名

    我正在尝试生成带有视觉签名和 pdfbox 的 PDF 我有两个流 似乎 pdfbox 只能处理文件 如果没有三个临时文件 我就无法使其工作 我可以看到从here https github com apache pdfbox blob b7