java - 使用PDFbox在pdf文件中用unicode文本替换字符串?
问题描述
我需要从 PDF 文件中读取字符串并将其替换为 Unicode 文本。如果是 ASCII 字符,一切都很好。但是对于 Unicode 字符,它会显示问号/垃圾文本。字体文件(ttf)没有问题我可以将 unicode 文本写入具有不同类(PDFContentStream)的 pdf 文件。使用这个类,没有替换文本的选项,但我们可以添加新文本。
示例 Unicode 文本
咩咩
问题(地址栏)
https://drive.google.com/file/d/1DbsApTCSfTwwK3txsDGW8sXtDG_u-VJv/view?usp=sharing
我正在使用 PDFBox。请在这件事上给予我帮助.....
检查我正在使用的代码.....
enter image description herepublic static PDDocument _ReplaceText(PDDocument document, String searchString, String replacement)
throws IOException {
if (StringUtils.isEmpty(searchString) || StringUtils.isEmpty(replacement)) {
return document;
}
for (PDPage page : document.getPages()) {
PDResources resources = new PDResources();
PDFont font = PDType0Font.load(document, new File("arial-unicode-ms.ttf"));
//PDFont font2 = PDType0Font.load(document, new File("avenir-next-regular.ttf"));
resources.add(font);
//resources.add(font2);
//resources.add(PDType1Font.TIMES_ROMAN);
page.setResources(resources);
PDFStreamParser parser = new PDFStreamParser(page);
parser.parse();
List tokens = parser.getTokens();
for (int j = 0; j < tokens.size(); j++) {
Object next = tokens.get(j);
if (next instanceof Operator) {
Operator op = (Operator) next;
String pstring = "";
int prej = 0;
// Tj and TJ are the two operators that display strings in a PDF
if (op.getName().equals("Tj")) {
// Tj takes one operator and that is the string to display so lets update that
// operator
COSString previous = (COSString) tokens.get(j - 1);
String string = previous.getString();
string = string.replaceFirst(searchString, replacement);
previous.setValue(string.getBytes());
} else if (op.getName().equals("TJ")) {
COSArray previous = (COSArray) tokens.get(j - 1);
for (int k = 0; k < previous.size(); k++) {
Object arrElement = previous.getObject(k);
if (arrElement instanceof COSString) {
COSString cosString = (COSString) arrElement;
String string = cosString.getString();
if (j == prej) {
pstring += string;
} else {
prej = j;
pstring = string;
}
}
}
if (searchString.equals(pstring.trim())) {
COSString cosString2 = (COSString) previous.getObject(0);
cosString2.setValue(replacement.getBytes());
int total = previous.size() - 1;
for (int k = total; k > 0; k--) {
previous.remove(k);
}
}
}
}
}
// now that the tokens are updated we will replace the page content stream.
PDStream updatedStream = new PDStream(document);
OutputStream out = updatedStream.createOutputStream(COSName.FLATE_DECODE);
ContentStreamWriter tokenWriter = new ContentStreamWriter(out);
tokenWriter.writeTokens(tokens);
out.close();
page.setContents(updatedStream);
}
return document;
}
解决方案
您的代码完全破坏了 PDF,参见。Adobe Preflight 输出:
原因很明显,你的代码
PDResources resources = new PDResources();
PDFont font = PDType0Font.load(document, new File("arial-unicode-ms.ttf"));
resources.add(font);
page.setResources(resources);
删除预先存在的页面资源,您的替换只包含一种字体,您允许 PDFBox 任意选择其名称。
您不得删除现有资源,因为它们在您的文档中使用。
检查 PDF 页面的内容很明显,最初使用的字体T1_0和T1_1的编码是单字节编码或混合的单/多字节编码;较低的单字节值似乎是类似 ASCII 的编码。
我会假设编码是WinAnsiEncoding或其子集。作为推论你的任务
从 PDF 文件中读取字符串并将其替换为 Unicode 文本
不能作为简单的替换来实现,至少不能考虑任意 Unicode 代码点。
你可以实现的是:
- 首先通过自定义文本剥离器运行您的源 PDF,而不是提取纯文本搜索您的字符串以替换并返回它们的位置。这里有许多问题和答案,向您展示了如何确定文本剥离器子类中字符串的坐标,最近的一个是这个。
- 接下来从您的 PDF 中删除这些原始字符串。在您的情况下,类似于您上面的原始代码的方法(显然不会丢弃资源),用同样长的空格字符串替换字符串可能会起作用,即使它是一个肮脏的黑客。
PDFContentStream
最后使用附加模式在确定的位置添加替换;为此,将您的新字体添加到现有资源中。
但请注意,PDF 不是为这样使用而设计的。模板 PDF 可以用作新内容的背景,但尝试替换其中的内容通常是一种糟糕的设计,会导致麻烦。如果您需要在模板中标记位置,请使用在填写过程中可以轻松删除的注释。或者使用原生 PDF 表单技术 AcroForm 表单。
推荐阅读
- push-notification - 桌面和手机上的 Web 推送
- python - 您如何优化此代码以使其更小?
- python - sqlalchemy InvalidRequestError:一个或多个映射器无法初始化 - 无法继续初始化其他映射器
- mysql - MySQL Workbench 中使用外键出错,错误代码 1064
- python - Python Pandas groupby:根据值的条件过滤和应用
- android - 如何在一个登录页面中有多个用户
- c - 将值分配给结构数组
- vba - 错误 1004 无论如何 - 从第一个到最后一个动态
- swift - SWIFT:WKWebView:填写登录数据
- sprite - Godot - 我可以像素化一个节点(Sprite)吗?