答案在 Java 中,但转换为 C# 应该是一些简单的字母大小写替换和小调整。
不幸的是,这里没有灵丹妙药的解决方案,至少在不付出巨大努力的情况下并非如此。
1。偏正解
这里有几个问题。首先,您只更新了/Contents 键,而您正在编辑的注释也有/RC 键,代表A rich text string (see Adobe XML Architecture, XML Forms Architecture (XFA) Specification, version 3.3) that shall be used to generate the appearance of the annotation. (ISO 32000)。
除此之外,必须重新生成外观(/AP 条目)。正如规范所规定的那样。这不是 iText 目前能够做的,所以你必须自己做。
您需要确定必须绘制文本的区域,将/RD 或 rect diff 条目考虑在内。
要创建您的外观,您可以使用pdfHTML 附加组件,它将来自/RC 的富文本表示处理为布局元素,您可以将其传输到可以放入/AP 的XObject。
代码类似如下:
PdfDocument pdfDocument = new PdfDocument(new PdfReader("in PDF.pdf"),
new PdfWriter("out PDF.pdf"));
int numberOfPages = pdfDocument.getNumberOfPages();
for (int i = 1; i <= numberOfPages; i++) {
PdfDictionary page = pdfDocument.getPage(i).getPdfObject();
PdfArray annotArray = page.getAsArray(PdfName.Annots);
if (annotArray == null) {
continue;
}
int size = annotArray.size();
for (int x = 0; x < size; x++) {
PdfDictionary curAnnot = annotArray.getAsDictionary(x);
if (curAnnot.getAsString(PdfName.Contents) != null) {
String contents = curAnnot.getAsString(PdfName.Contents).toString();
if (!contents.isEmpty() && contents.contains("old content")) //set layer for a FreeText with this content
{
curAnnot.put(PdfName.Contents, new PdfString("new content"));
String richText = curAnnot.getAsString(PdfName.RC).toUnicodeString();
Document document = Jsoup.parse(richText);
for (Element element : document.select("p")) {
element.html("new content");
}
curAnnot.put(PdfName.RC, new PdfString(document.body().outerHtml()));
Rectangle bbox = curAnnot.getAsRectangle(PdfName.Rect);
Rectangle textBbox = bbox.clone();
// left, top, right, bottom
PdfArray rectDiff = curAnnot.getAsArray(PdfName.RD);
if (rectDiff != null) {
textBbox.applyMargins(rectDiff.getAsNumber(1).floatValue(),
rectDiff.getAsNumber(2).floatValue(),
rectDiff.getAsNumber(3).floatValue(),
rectDiff.getAsNumber(0).floatValue(), false);
}
float leftRectDiff = rectDiff != null ? rectDiff.getAsNumber(0).floatValue() : 0;
float topRectDiff = rectDiff != null ? rectDiff.getAsNumber(1).floatValue() : 0;
List<IElement> elements = HtmlConverter.convertToElements(document.body().outerHtml());
PdfFormXObject appearance = new PdfFormXObject(
new Rectangle(0, 0, bbox.getWidth(), bbox.getHeight()));
Canvas canvas = new Canvas(new PdfCanvas(appearance, pdfDocument),
new Rectangle(leftRectDiff, topRectDiff, textBbox.getWidth(), textBbox.getHeight()));
canvas.setProperty(Property.RENDERING_MODE, RenderingMode.HTML_MODE);
for (IElement ele : elements) {
if (ele instanceof IBlockElement) {
canvas.add((IBlockElement) ele);
}
}
curAnnot.getAsDictionary(PdfName.AP).put(PdfName.N, appearance.getPdfObject());
}
}
}
}
pdfDocument.close();
你会得到如下所示的结果:
您可以看到新文本按预期显示,但整体视觉表现与我们的预期相差甚远——缺少背景填充、边框和箭头。因此,要正确生成外观,您必须进一步探索其他 PDF 属性,例如 /CL(箭头描述符)、/BS(边框样式)、/C(背景颜色)等。这需要相当长的时间 - 阅读在规范上,解析相关条目并将其应用到您的绘图操作中。你可以从PdfFormField类的实现中得到一些启发。
2。没有任何保证的简单解决方案
如果您希望注释中的文本仅包含一行,是纯拉丁文本,并且通常输入文档的可变性很小,您可以采用当前外观并假设文本字符串将被写入有一大块(你的输入文档就是这种情况)。
请注意,这是一种 hacky 方法,容易出现许多潜在错误/错误。
示例代码:
PdfDocument pdfDocument = new PdfDocument(new PdfReader("in PDF.pdf"),
new PdfWriter("out PDF.pdf"));
int numberOfPages = pdfDocument.getNumberOfPages();
for (int i = 1; i <= numberOfPages; i++) {
PdfDictionary page = pdfDocument.getPage(i).getPdfObject();
PdfArray annotArray = page.getAsArray(PdfName.Annots);
if (annotArray == null) {
continue;
}
int size = annotArray.size();
for (int x = 0; x < size; x++) {
PdfDictionary curAnnot = annotArray.getAsDictionary(x);
if (curAnnot.getAsString(PdfName.Contents) != null) {
String contents = curAnnot.getAsString(PdfName.Contents).toString();
String oldContent = "old content";
if (!contents.isEmpty() && contents.contains(oldContent)) {
String newContent = "new content";
curAnnot.put(PdfName.Contents, new PdfString(newContent));
String richText = curAnnot.getAsString(PdfName.RC).toUnicodeString();
Document document = Jsoup.parse(richText);
for (Element element : document.select("p")) {
element.html(newContent);
}
curAnnot.put(PdfName.RC, new PdfString(document.body().outerHtml()));
PdfStream currentAppearance = curAnnot.getAsDictionary(PdfName.AP).getAsStream(PdfName.N);
String currentBytes = new String(currentAppearance.getBytes(), StandardCharsets.UTF_8);
currentBytes = currentBytes.replace("(" + oldContent + ") Tj", "(" + newContent + ") Tj");
currentAppearance.setData(currentBytes.getBytes(StandardCharsets.UTF_8));
}
}
}
}
pdfDocument.close();
视觉结果(如您所见,这就是我们想要的):
3。不合规解决方案
另一种不符合 PDF 规范的方法是删除 /AP 条目。您可以使用curAnnot.remove(PdfName.AP); 在同一个循环中执行此操作。大多数主要的 PDF 查看器都将自行重新生成外观。但是,我的查看器生成的外观并不是最吸引人的:
如您所见,结果将取决于 PDF 查看器,这很好地说明了 PDF 规范要求存在 /AP 的原因。 再次强调,这种方式不符合 PDF 规范。