5

更新:2021-01-15 - 增加了赏金

我正在尝试更改编校注释以更改在应用编校时刻录到 PDF 中的基础文本。在 Acrobat 中,您可以设置“编校代码”的集合,这些代码可用于确定您将某些内容标记为已编校的原因。我的目标是用系统定义的值覆盖用户选择的内容。代码将在应用编辑之前运行。

在我的尝试中,我发现将光标悬停在编辑框上时 Acrobat 产品中可用的“预览”是 Acrobat 独有的,大多数其他查看器不会显示预览。似乎预览与应用的实际编辑分开维护。我不需要更改预览中显示的文本,只需更改应用密文后显示的内容即可。

我已经增加了 150 声望的赏金,因为我认为我自己无法找到解决方案。我最初的问题指定了 iText7,因为这是在我自己的尝试中让我最接近的库。虽然我更喜欢使用 iText7,但我也会考虑使用我可以合理访问的其他库的解决方案(如果需要,我确实有少量预算可以用来购买另一个库)。

我保留了我最初的问题以及我在下面亲自尝试过的后续内容。我感谢提供的任何帮助。

如果您需要一个样本进行测试,这个 DropBox文件夹有一个名为的文件01 - Original.pdf,您可以将其用作源文档。期望的结果是能够更改在将“原始覆盖文本”的编辑应用到任何其他值(例如“新文本”)时出现的文本。

原始问题:

我正在尝试更改 PDF 中每个修订注释中包含的文本,使用iText7. 该PdfRedactAnnotation对象有一个名为的方法SetOverlayText(),看起来它应该做我想做的事。因此,我编写了一个打开 PDF 的方法,循环浏览页面,然后循环浏览每个页面上的注释,并检查注释是否为PdfRedactAnnotation. 如果是,它会调用SetOverlayText().

在调试和查看注释属性时,我可以看到OverlayText肯定发生了变化。但是,当我打开文件并通过将光标悬停在编辑标记上来检查覆盖文本时,原始覆盖文本仍然存在。

在此处输入图像描述

此外,如果我应用了编辑,原始的覆盖文本就是被烧入页面的内容。

但是,当我右键单击注释时(在应用编辑之前),覆盖文本会立即更新为新文本:

在此处输入图像描述

在这一点上,当我应用编辑时,将新文本刻录到 PDF 中。

有什么方法可以以编程方式触发修订注释更新,而无需打开并右键单击每个注释?我在下面包含了我的代码。感谢您提供任何人可以提供的任何建议。

PdfDocument pdfDoc = new PdfDocument(new PdfReader(@"C:\temp\Test - Original.pdf"), new PdfWriter(@"C:\temp\Test - Output.pdf"));
Document doc = new Document(pdfDoc);
int pageCount = pdfDoc.GetNumberOfPages();
for (int i = 1; i <= pageCount; i++)
{
    var annotations = pdfDoc.GetPage(i).GetAnnotations();
    foreach(var annotation in annotations)
    {
        if (annotation is PdfRedactAnnotation)
        {
            PdfRedactAnnotation redact = (PdfRedactAnnotation)annotation;
            redact.SetOverlayText(new PdfString("New Text"));
        }
    }
}
doc.Close();

更新:截至 2021 年 1 月 7 日的调查结果

正如@mkl 的回答所指出的那样,PDF Redact Annotation Specification 阐明了底层的 redact annotation DOM 条目。OverlayText只是等式的一部分。如果您使用OverlayText,则必须定义一个DA元素(DA 是一个为 OverlayText 提供格式信息的字符串)。最后,如果定义了RO,它将取代几乎所有其他独立的显示条目。

我的测试文档是使用 Acrobat DC Pro 制作的,通过在 Acrobat 中手动添加编辑。这样做会产生一个带有所有上述条目的编辑注释。我的测试文档的副本可以在这个 DropBox文件夹中找到。

(旁注:在我最初的问题中,我提到将鼠标悬停在编辑的红色矩形上以预览应用的编辑将是什么样子......在多个浏览器和其他 PDF 查看器(如 Foxit Reader)中进行测试后,它看起来像功能“预览”仅在 Acrobat 产品中支持通过将鼠标悬停在红色轮廓上应用编辑时的外观。所有其他测试的查看器将仅显示红色边框,当您将光标悬停在它上面时不会发生任何事情。黑色上面显示的矩形只能在应用编辑后在其他程序中查看。

附加测试表明,悬停预览与编辑详细信息本身是分开维护的,Acrobat 会尝试使悬停详细信息与基础注释保持同步。测试时最好忽略悬停预览,并在应用编辑后参考结果。)

@mkl 建议删除 RO 条目以尝试让 OverlayText 优先考虑是一个好主意,但不幸的是它没有奏效。与我原来的结果没有显着差异。

在 iText7 的 PdfRedactAnnotation 中四处寻找之后,我发现以下方法都会导致对 Redact 对象的 RO 条目的引用:

PdfRedactAnnotation redact = (PdfRedactAnnotation)annotation;
redact.GetRolloverAppearanceObject();
redact.GetRedactionRolloverAppearance();
redact.GetPdfObject().Get(PdfName.RO);
redact.GetAppearanceDictionary().Get(PdfName.R);

(我通过检查相等比较器确认它们实际上是完全相同的引用。作为引用类型,它们true在使用 测试时都返回==)。

在进一步的测试中,我得出的结论是 RO 属性必须具有内部存储的相同 OverlayText 的副本。如果您有两个具有不同原始值的编校,您可以将 RO 元素从一个编校“复制”到另一个编校:

PdfObject ro = firstRedact.GetPdfObject().Get(PdfName.RO);
secondRedact.GetPdfObject().Put(PdfName.RO, ro);

如果您这样做并应用编辑,第一个编辑中的“覆盖文本”将替换第二个中的“覆盖文本”。其他 RO 元素值也被复制(例如 BBox,它定义了黑色矩形的尺寸)......但至少可以调整这些元素。

问题仍然是 RO 的 iText7 PdfObject 有 7 个子元素,并且它们或它们的后代元素似乎都没有暴露我试图更改的文本。

我的最后一个测试是我是否可以将 RO 元素从一个 PDF 复制到另一个(以便我可以使用第二个源 PDF 和已配置所需 RO“覆盖文本”的注释),但看起来间接对象不喜欢将 .Put() 放入其他文档中。

所以现在,我只能尝试找到一种方法来访问/更改存储在 RO 中的文本,或者从另一个文档中克隆预配置的 RO。

4

2 回答 2

2

规范是怎么说的?

密文注释的OverlayText条目指定为

钥匙 类型 价值
覆盖文本 文本字符串 (可选)一个文本字符串,指定在删除受影响的内容后应在编辑区域上绘制的覆盖文本。如果存在RO条目,则忽略该条目。

(ISO 32000-2,表 195 - 特定于修订注释的附加条目)

也许在您的源 PDF 中,编校注释优先考虑RO 。

此外,该表说明了有关DA条目的内容:

钥匙 类型 价值
字节串 (如果存在OverlayText,则为必需,否则忽略)在删除受影响的内容后绘制覆盖文本时应用于格式化覆盖文本的外观字符串(参见 12.7.4.3,“可变文本”)。如果存在 RO 条目,则忽略该条目。

因此,如果您使用OverlayText,您还必须确保设置了DA默认外观字符串。你是否?


同一个表中的RO条目被指定为

钥匙 类型 价值
反渗透 溪流 (可选)一个表单 XObject,指定此密文注释的覆盖外观。应用此修订并删除受影响的内容后,应绘制覆盖外观,使其原点与注释矩形的左下角对齐。这种形式的 XObject 不一定与其他注释外观相关,并且可能存在也可能不存在于AP字典中。此条目优先于ICOverlayTextDAQ条目。

那么现在该怎么办呢?

根据上面发布的详细信息,一个明显的选择是为更改的编校注释创建一个编校覆盖 XObject ( RO )。您可以通过更换您的

if (annotation is PdfRedactAnnotation)
{
    PdfRedactAnnotation redact = (PdfRedactAnnotation)annotation;
    redact.SetOverlayText(new PdfString("New Text"));
}

经过

if (annotation is PdfRedactAnnotation)
{
    PdfRedactAnnotation redact = (PdfRedactAnnotation)annotation;
    redact.SetOverlayText(new PdfString("New Text"));
    Rectangle rectangle = redact.GetRectangle().ToRectangle();
    PdfStream stream = redact.GetRedactRolloverAppearance();
    if (stream != null)
    {
        rectangle = stream.GetAsArray(PdfName.BBox).ToRectangle();
    }
    PdfFormXObject redactionOverlay = new PdfFormXObject(rectangle);
    redactionOverlay.GetPdfObject().Put(PdfName.Matrix, new PdfArray(new double[] { 1, 0, 0, 1, -rectangle.GetX(), -rectangle.GetY() }));
    using (Canvas canvas = new Canvas(redactionOverlay, pdfDocument))
    {
        PdfCanvas pdfCanvas = canvas.GetPdfCanvas();
        pdfCanvas.SetFillColorGray(0);
        pdfCanvas.Rectangle(rectangle);
        pdfCanvas.Fill();
        pdfCanvas.SetFillColorGray(1);
        canvas.Add(new Paragraph("New Text"));
    }

    stream = redactionOverlay.GetPdfObject();
    redact.SetRolloverAppearance(stream);
    redact.SetDownAppearance(stream);
    redact.SetRedactRolloverAppearance(stream);
}

在 Acrobat 中编辑后的结果:

截屏

通过调整使用的填充颜色段落样式,您可以使外观更接近于 Adob​​e Acrobat 生成的外观(或者您也可以完全按照自己的设计生成外观)。

请注意,我只有一个相当旧的 Adob​​e Acrobat 版本,v9.5,所以当前版本可能不接受上面生成的编辑外观,或者至少以不同的方式应用它。

于 2021-01-06T16:01:17.350 回答
0

我能够更改编校注释覆盖文本,并在编校时使该文本正确显示在编校块上。我使用了作为SyncFusion File Formats一部分包含的 SyncFusion Essential PDF 库。(我不隶属于 SyncFusion,尽管我确实通过我的雇主获得了他们的文件格式库的付费许可。)我使用 Adob​​e Acrobat Pro DC 进行了测试。

当我第一次尝试替换编辑覆盖文本时,我遇到了与 SyncFusion 类似的问题,就像 OP 对 iText 7 所做的那样:运行我的代码后,覆盖将显示为已更改,但编辑会带回以前替换的覆盖文本。由于无法同时更改显示的文本覆盖和编辑过程可访问的覆盖文本,我通过编写代码来解决这个问题,进行所需的更改,将 PDF 的注释导出到 JSON 文件,删除 PDF 的注释,然后将 JSON 文件重新导入 PDF。这会为文本覆盖和编辑过程生成具有相同文本值的新注释(我相信,编辑过程覆盖文本是创建 PDF 注释的结果)。

using System.Drawing;
using Syncfusion.Pdf.Graphics;
using Syncfusion.Pdf.Interactive;
using Syncfusion.Pdf.Parsing;
using Syncfusion.Pdf;

PdfLoadedDocument loadedDocument = new PdfLoadedDocument(@"C:\Users\Joe\Desktop\Redact\MarkedOriginal.pdf");
PdfLoadedPage page = loadedDocument.Pages[0] as PdfLoadedPage;
foreach (PdfLoadedRedactionAnnotation redactionAnnotation in loadedDocument.Pages[0].Annotations)
{
    PdfStandardFont font = new PdfStandardFont(PdfFontFamily.Helvetica, 10);
    redactionAnnotation.Font = font;
    redactionAnnotation.TextColor = Color.White;
    redactionAnnotation.BorderColor = Color.Black; //See note in SO answer about this
    redactionAnnotation.OverlayText = "New Text";
}

//Export, delete, and then import annotations to create a redaction annotation with the same preview and final redaction
loadedDocument.ExportAnnotations(@"C:\Users\Joe\Desktop\Redact\Output.json", AnnotationDataFormat.Json);

for (int i = 1; i <= loadedDocument.Pages[0].Annotations.Count; i++)
{
    loadedDocument.Pages[0].Annotations.RemoveAt(i);
}
loadedDocument.ImportAnnotations(@"C:\Users\Joe\Desktop\Redact\Output.json", AnnotationDataFormat.Json);
loadedDocument.Save();
loadedDocument.Close(true);

如果 OP 需要编辑标记框的边框是黑色以外的颜色,则需要编写更多代码。我发现当我使用redactionAnnotation.BorderColor = Color.Black;编辑标记框时,它看起来像预期的那样。但是,当我使用Color.Red或其他颜色时,边框保留了黑色,新颜色也与第一个修订接壤,并且只有黑色与 OP 提供的文件中的第二个修订接壤。通过进一步研究,我怀疑这可以通过 SyncFusion、iText 7 或可能通过defaultappearance在将文件导入 PDF 之前编辑 JSON 文件的注释行来解决。这是defaultappearance我运行代码时生成的行:

"defaultappearance": "1 1 1 RG 0 g 0 Tc 0 Tw 100 Tz 0 TL 0 Ts 0 Tr /Helv 10 Tf"

值得指出的是,SyncFusion 有免费和付费的软件许可等级。根据SyncFusion, SyncFusion 社区许可证对“年总收入低于 100 万美元且开发人员人数不超过 5 人的公司和个人”免费。SyncFusion文件格式 开发人员许可证将涵盖其他所有人。

于 2021-01-19T02:08:36.850 回答