【问题标题】:How to change redaction text of a redact annotation created in Adobe Acrobat如何更改在 Adob​​e Acrobat 中创建的编校注释的编校文本
【发布时间】:2021-03-31 13:53:43
【问题描述】:

更新:2021-01-15 - 添加赏金

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

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

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

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

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

原问题:

我正在尝试使用 iText7 更改 PDF 中每个编校注释中包含的文本。 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 编校注释规范阐明了底层编校注释 DOM 条目。 OverlayText 只是其中的一部分。如果您使用 OverlayText,则必须定义一个 DA 元素(DA 是一个为 OverlayText 提供格式信息的字符串)。最后,如果定义了 RO,它将取代几乎所有其他独立的显示条目。

我的测试文档是使用 Acrobat DC Pro 制作的,通过在 Acrobat 中手动添加编辑。这样做会产生一个带有所有上述条目的编辑注释。我的测试文档的副本可以在this 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。

【问题讨论】:

    标签: c# itext7 redaction


    【解决方案1】:

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

    当我第一次尝试替换编校覆盖文本时,我在 SyncFusion 中遇到了与 OP 对 iText 7 类似的问题:运行我的代码后,覆盖将显示为已更改,但编校会带回以前替换的覆盖文本。由于无法同时更改显示的文本覆盖和编辑过程可访问的覆盖文本,我通过编写代码来解决这个问题,进行所需的更改,将 PDF 的注释导出到 JSON 文件,删除 PDF 的注释,然后将 JSON 文件重新导入 PDF。这会为文本覆盖和编辑过程生成具有相同文本值的新注释(我相信,编辑过程覆盖文本是创建 PDF 注释的结果)。这是使用 SyncFusion Essential 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 或可能通过在将文件导入 PDF 之前编辑 JSON 文件的注释 defaultappearance 行来解决。这是我运行代码时生成的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 Community License 对“年总收入低于 100 万美元且开发人员人数不超过 5 人的公司和个人”免费。 SyncFusion File Formats Developer License 将覆盖其他所有人。

    【讨论】:

      【解决方案2】:

      规范是怎么说的?

      密文注释的OverlayText条目指定为

      Key Type Value
      OverlayText text string (Optional) A text string specifying the overlay text that should be drawn over the redacted region after the affected content has been removed. This entry is ignored if the RO entry is present.

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

      也许在您的源 PDF 中,编校注释具有 RO 优先权。

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

      Key Type Value
      DA byte string (Required if OverlayText is present, ignored otherwise) The appearance string that shall be used in formatting the overlay text when it is drawn after the affected content has been removed (see 12.7.4.3, "Variable text"). This entry is ignored if the RO entry is present.

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


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

      Key Type Value
      RO stream (Optional) A form XObject specifying the overlay appearance for this redaction annotation. After this redaction is applied and the affected content has been removed, the overlay appearance should be drawn such that its origin lines up with the lower-left corner of the annotation rectangle. This form XObject is not necessarily related to other annotation appearances, and may or may not be present in the AP dictionary. This entry takes precedence over the IC, OverlayText, DA, and Q entries.

      那么现在该怎么办?

      根据上面发布的详细信息,一个明显的选择是为更改的编校注释创建编校覆盖 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,所以当前版本可能不接受上面或在至少以不同的方式应用它。

      【讨论】:

      • 感谢您提供参考信息,它非常有用。虽然这些特定属性无法解决问题,但我能够找到 PDF 文档,以便能够进一步试验并找到解决方法。我很快就会发布一个答案,描述我是如何让它工作的。
      • 更新:看起来原始 OverlayText 的副本保存在 AppearanceDictionary 的翻转条目中的某个位置。最初,我认为删除 AppearanceDictionary 解决了我的问题。然而,这导致了一系列故障。所以我认为我需要更新 .GetRolloverAppearance() 返回的 ApearanceDictionary 以更新那里的 OverlayText,但 iText 并没有公开所有必要的编辑外观属性来根据我的需要进行修改。我可能不走运......
      • 您是否尝试过简单地删除该 RolloverAppearance? (实际上我认为这是一个错误命名的吸气剂。它指的是我的答案中描述的 RO 条目,它与 rollover 无关,但更可能是 编辑覆盖。)
      • 我确实尝试删除它。 GetRolloverAppearanceObject()、GetRedactionRolloverAppearance()、GetPdfObject().Get(PdfName.RO) 和 GetAppearanceDictionary().Get(PdfName.R) 都返回相同的元素:编校覆盖的 PdfStream 或 PdfDicitonary。 Remove(PdfName.RO) 产生的效果与我原来的问题中描述的效果相同。 GetPdfObject().GetAsDictionary(PdfName.AP).Remove(PdfName.R) 根本没有翻转效果。 Remove(PdfName.AP) 在 Acrobat 中打开时似乎可以工作,但它正在注入默认的 AppearanceDictionary 来修复问题。其他观看者没有显示任何内容
      • 谢谢您,您的代码按原样运行,它帮助我更好地了解使用 iText7 的新方法。感谢您为帮助我而付出的所有时间和精力!
      猜你喜欢
      • 2017-02-20
      • 1970-01-01
      • 1970-01-01
      • 2010-09-11
      • 2013-08-02
      • 1970-01-01
      • 1970-01-01
      • 2019-01-02
      • 2018-12-06
      相关资源
      最近更新 更多