1
votes

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

1
You update the value of the /Contents entry, but I don't see you changing the appearance (/AP) anywhere. - Bruno Lowagie
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
@BrunoLowagie I also found this stackoverflow.com/questions/36902331/… 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
@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
Ok figured it out! Thanks for your assistance. This example also helped show how to update stream object bytes using the SetData() method. developers.itextpdf.com/examples/stamping-content-existing-pdfs/… - James Cramer

1 Answers

1
votes

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;
}