1

I would like to update text content within a FreeText annotation when I copy the annotation from one PDF document to another, but for some reason the text does not update in the final PDF using the approach shown below. The annotation object updates, but the final result within the PDF does not reflect the updated content for the FreeText annotation type. Strangely, Ink type annotations do get updated with the revised content, as it shows up in the form of a sticky note looking comment overlaid on top of the Ink annotation itself.

Here's a quick snippet of the code I've used (if needed I can add more):

foreach (var anno in annots)
{
    var a = anno.GetPdfObject().CopyTo(masterPdfDoc);

    PdfAnnotation ano = PdfAnnotation.MakeAnnotation(a);
    var contents = ano.GetContents().ToString();
    ano.SetContents(new PdfString("COMMENT: " + contents));
    //ano.Put(PdfName.Contents, new PdfString("COMMENT: " + contents));

    masterDocPage.AddAnnotation(ano);
}

Would greatly appreciate any advice provided. Thanks

James Cramer
  • 455
  • 3
  • 16
  • 1
    You update the value of the `/Contents` entry, but I don't see you changing the appearance (`/AP`) anywhere. – Bruno Lowagie Aug 27 '17 at 17:36
  • Thanks for the fast response Bruno! Nice to get a response from the creator :-) I'm pretty new to itext7 and the PDF specs in general, so I don't understand how the appearance will have an effect on text content being presented in the text annotation. It seems that I should just be able to update the content and it is reflected. I took a look at the data using the GetAppearanceDictionary() and GetAppearanceCharacteristics() and I can't figure out what I would need to change there. – James Cramer Aug 27 '17 at 19:03
  • @BrunoLowagie I also found this https://stackoverflow.com/questions/36902331/cant-change-contents-of-annotation post, but wasn't able to make anything work. I tried looking at the DA, N, etc but it isn't apparent to me what I am supposed to change. I also tried removing the AP altogether as was suggested in the marked answer via `ano.Remove(PdfName.AP);`. – James Cramer Aug 27 '17 at 21:14
  • @InTheRed, When copying stuff, and changing the raw contents of an annotation, those changes aren't automatically relfected. And since it's the appearance dictionnary and not /Contents that dictates visuals... Many of the high-level methods in iText will actually update appearances (for example, when form-filling), but you're using low-level manipulation. It's possible to retrieve the appearance directly and manipulate it (via `PdfAnnotation#getAppearanceDictionary()` – Samuel Huylebroeck Aug 28 '17 at 09:11
  • @SamuelHuylebroeck Here is the Dictionary data for a Callout Annotation that says "Add a stop sign". {[/N, <> /ProcSet [/PDF /Text ] >> /Subtype /Form /Type /XObject >>]} – James Cramer Aug 28 '17 at 16:33
  • @SamuelHuylebroeck Ok I am able to extract the text from the appearance stream using code similar to what is shown here: https://stackoverflow.com/questions/37014984/how-to-read-text-of-appearance-stream?noredirect=1&lq=1 This shows how to extract text, but how would you insert or edit text? Do I need to edit the raw bytes? – James Cramer Aug 28 '17 at 18:39
  • 1
    Ok figured it out! Thanks for your assistance. This example also helped show how to update stream object bytes using the SetData() method. http://developers.itextpdf.com/examples/stamping-content-existing-pdfs/clone-replacing-pdf-objects – James Cramer Aug 28 '17 at 23:45

1 Answers1

2

The following code snippet copies and modifies the text content of FreeText annotations from 1 PDF (i.e. annots) and saves the modified annotations into a new PDF. A good chunk of the code is similar to the answer of this post but was updated for iText7.

foreach (var anno in annots)
{
    var a = anno.GetPdfObject().CopyTo(masterPdfDoc);
    PdfAnnotation ano = PdfAnnotation.MakeAnnotation(a);

    var apDict = ano.GetAppearanceDictionary();
    if (apDict == null)
    {
        Console.WriteLine("No appearances.");
        continue;
    }
    foreach (PdfName key in apDict.KeySet())
    {
        Console.WriteLine("Appearance: {0}", key);
        PdfStream value = apDict.GetAsStream(key);
        if (value != null)
        {
            var text = ExtractAnnotationText(value);
            Console.WriteLine("Extracted Text: {0}", text);

            if (text != "")
            {
                var valueString = Encoding.ASCII.GetString(value.GetBytes());
                value.SetData(Encoding.ASCII.GetBytes(valueString.Replace(text, "COMMENT: " + text)));
            }
        }
    }
    masterDocPage.AddAnnotation(ano);
}

public static String ExtractAnnotationText(PdfStream xObject)
{
   PdfResources resources = new PdfResources(xObject.GetAsDictionary(PdfName.Resources));
   ITextExtractionStrategy strategy = new LocationTextExtractionStrategy();

   PdfCanvasProcessor processor = new PdfCanvasProcessor(strategy);
   processor.ProcessContent(xObject.GetBytes(), resources);
   var text = strategy.GetResultantText();
   return text;
}
James Cramer
  • 455
  • 3
  • 16