【问题标题】:How to sanitise a string before printing it to PDF with PDFBox如何在使用 PDFBox 将字符串打印到 PDF 之前对其进行清理
【发布时间】:2017-07-15 10:03:33
【问题描述】:

我正在从 UTF-8 的用户输入创建 PDF 文档。

除了显示 PDF 之外,创建本身也会失败并显示 java.lang.IllegalArgumentException: U+039B is not available in this font's encoding: WinAnsiEncoding

这里的大多数答案都指向“使用具有更好 UTF-8 支持的字体”,但由于我无法控制用户输入,因此这种 UTF-8 支持永远不够好,我需要一个防弹解决方案(就像打印一些东西而不是错误输出)。

答案Using PDFBox to write unicode strings to a PDF 建议在将文本添加到 PDF 之前对其进行清理。

问题是我找不到有效的例子来实现这一点。 所有示例似乎都指向已删除的代码(font.setToUnicode或某种编码方法,一次转换一个字符)。

所以简而言之,我有一个字符串,我想要一个防弹方法将大部分写入 PDFBox 文档(显然,字体中缺少的字符将被替换或不打印)。

非常感谢, 杰姆

【问题讨论】:

  • 您使用哪个 PDFBox 版本?正如您所指的答案所指出的,1.8.x 和 2.0.x 版本的情况不同
  • 我使用的是 2.0.3(最后发布的)。
  • 您使用哪种字体?你如何使用它? Pdfbox 2.0.x 允许您嵌入包含所需字形的字体子集。
  • @mkl 是的,我尝试使用 Ubuntu 字体,这在一定程度上改进了一些东西,但它永远不够好,因为我无法提前知道将打印哪些字符。我正在打印作为用户输入的文本,基本上他们可以访问整个 UTF-8 集。有没有办法知道字体中的字形是什么代码点?那将是非常低效的,但我可以扫描所有字符串并用place hlder替换丢失的字符......
  • stackoverflow.com/a/31424164/3977077 这对删除不可打印字符很有帮助

标签: java pdf encoding utf-8 pdfbox


【解决方案1】:

我结束了逐个字符的清理工作。

这是我的清理功能的样子。

为避免重新处理字符,我缓存了每个给定字体的每个字符的可用性。

当字体中的代码点不可用时,我正在尝试“标准”替换字符,如果不可用,我将用问号替换。

这确实是低效的,但我还没有找到另一种更有效的方法来做到这一点,因为我无法控制,也无法预先了解正在打印的内容。

可能有很多地方需要改进,但这适用于我的用例。

private String getPrintableString(String string, PDFont font) {

    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < string.length(); i++) {

        int codePoint = string.codePointAt(i);

        if (codePoint == 0x000A) {
            sb.appendCodePoint(codePoint);
            continue;
        }

        String fontName = font.getName();
        int cpKey = fontName.hashCode();
        cpKey = 31 * cpKey + codePoint;

        if (codePointAvailCache.get(cpKey) == null) {

            try {
                font.encode(string.substring(i, i + 1));
                codePointAvailCache.put(cpKey, true);
            } catch (Exception e) {
                codePointAvailCache.put(cpKey, false);
            }
        }

        if (!codePointAvailCache.get(cpKey)) {

            // Need to make sure our font has a replacement character
            try {
                codePoint = 0xFFFD;
                font.encode(new String(new int[] { codePoint }, 0, 1));
            } catch (Exception e) {
                codePoint = 0x003F;
            }
        }

        sb.appendCodePoint(codePoint);
    }

    return sb.toString();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-15
    • 1970-01-01
    • 2012-06-13
    • 2013-08-15
    • 2019-11-11
    • 2013-09-09
    • 1970-01-01
    • 2015-04-18
    相关资源
    最近更新 更多