How can I use FlowDocument class to create a custom annotation appearance?

Question:
I would like to use the FlowDocument class to make a custom appearance for a DigitalSignature, how can I do that?

Answer:
You can use the FlowDocument class to generate a single page PDF object, and you can then re-use the appearance stream of that Page as the appearance of any annotation, including Widget annotations such as a DigitalSignature.

The following code will use the FlowDocument class to layout a single page, with two lines of text, vertically and horizontally aligned.

Then this page appearance is used as the appearance of a RubberStamp annotation.

Note: The BBox width/height of the appearance is scaled to match the width/height of the Rect. To avoid scaling they should match, but as long as the aspect ratio matches, then the scaling wont be a visible issue, except perhaps for images in the appearance stream.

// dimensions of custom appearance in 1/72 inch/PT unit.
double width = 144;
double height = 72;

var flowDocument = new FlowDocument();
flowDocument.SetDefaultPageSize(width, height);
flowDocument.SetDefaultMargins(0, 0, 0, 0);
var table = flowDocument.AddTable();
table.SetDefaultColumnWidth(width);
table.SetDefaultRowHeight(height);
var row = table.AddTableRow();
var cell = row.AddTableCell();
uint x = table.GetNumColumns();
uint y = table.GetNumRows();
cell.SetVerticalAlignment(TableCell.CellAlignmentVertical.e_center);
var paragraph = cell.AddParagraph("First row\nsecond longer row");
paragraph.SetJustificationMode(Paragraph.TextJustification.e_center);                
var doc = flowDocument.PaginateToPDF();

var targetPDF = new PDFDoc();
targetPDF.PagePushBack(targetPDF.PageCreate());

var importList = new ArrayList();
importList.Add(doc.GetPage(1));
var importedList = targetPDF.ImportPages(importList);
var importedPage = (Page)importedList[0];
var appStream = importedPage.GetContents();


// to avoid scaling, the aspect ratio of the Annotation should match the aspect ratio of the FlowDocument/BBox appearance.
// Either way, PDF rendering will scale the BBox appearance to the Annotation Rect.
// To avoid scaling, the BBox width/height and Rect width/height need to match

appStream.PutRect("BBox", 0, 0, width, height); // match the original FlowDocument page
double dx = 72;
double dy = 500;
var rubberStamp = RubberStamp.Create(targetPDF, new Rect(dx, dy, dx + width, dy + height)); // same aspect ratio
rubberStamp.SetAppearance(appStream);
appStream.PutName("Subtype", "Form");
appStream.PutName("Type", "XObject");
appStream.Put("Resources", importedPage.GetSDFObj().FindObj("Resources"));
targetPDF.GetPage(1).AnnotPushBack(rubberStamp);

targetPDF.Save("output.pdf", SDFDoc.SaveOptions.e_linearized);