Creating custom RubberStamp Annotation Appearance-Stream with PdfSharp

93 Views Asked by At

I have been using PdfSharp library in a C# project for saving PDF files with Annotations. I would like to create a RubberStamp Annotation with a custom image. PfSharp has a nice RubberStamp implementation, but it does not let you specify custom images, only the predefined icons like TopSecret.

I have tried to analyze other PDF files with custom Stamps and tried to mimic the same structure as they have, by manually creating the XObjects, adding the image to the Resources, and creating an Appearance Stream.

Long story short, it's not working. I have a feeling that simply writing the object to the Appearance-Stream is not a good idea, even though the image gets to the file, I can see the annotation rectangle in the PDF Viewer, but it's a blank white rect, showing nothing.

Ideally, I would like to trigger PDFSharp to create the Appearance Stream, but after several hours of online research, I haven't found anything useful.

Can anyone point me out how can I add a custom image to the RubberStamp Appearance-Stream? Or trigger the PdfSharp to recreate the Appearance-Stream?

public void SavePdf()
{
    System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

    // Create a new PDF document
    PdfDocument pdfDocument = new PdfDocument();
    pdfDocument.Version = 15;

    // Create an empty page
    PdfPage pdfPage;
    pdfPage = pdfDocument.AddPage();
    pdfPage.Width = XUnit.FromInch(8.5);
    pdfPage.Height = XUnit.FromInch(11.00);

    // Get an XGraphics object for drawing
    XGraphics gfx = XGraphics.FromPdfPage(pdfPage);

    // Creating the Rubber Stamp with some default settings
    PdfRubberStampAnnotation cstamp = new PdfRubberStampAnnotation() { Icon = PdfRubberStampAnnotationIcon.TopSecret };
    XRect rect = gfx.Transformer.WorldToDefaultPage(new XRect(new XPoint(10, 10), new XSize(200, 50)));
    cstamp.Rectangle = new PdfRectangle(rect);
    cstamp.Color = XColor.FromArgb(255, 0, 0);

    // Load the custom image
    var image = XImage.FromFile(@"c:\temp\test.bmp");
    var pdfImage = new PdfImage(pdfDocument, image);
    string r = pdfPage.Resources.AddImage(pdfImage); // adding the image to the Page References

    // Now let's create the XForm object, and reference the image
    var matrix = new PdfArray();
    matrix.Elements.Add(new PdfReal(1));
    matrix.Elements.Add(new PdfReal(0));
    matrix.Elements.Add(new PdfReal(0));
    matrix.Elements.Add(new PdfReal(1));
    matrix.Elements.Add(new PdfReal(0));
    matrix.Elements.Add(new PdfReal(0));

    var xform = new PdfDictionary(pdfDocument);
    xform.Elements.Add("/Type", new PdfName("/XObject"));
    xform.Elements.Add("/Subtype", new PdfName("/Form"));
    xform.Elements.Add("/FormType", new PdfInteger(1));
    xform.Elements.Add("/BBox", new PdfRectangle(new XRect(10, 10, image.PointWidth, image.PointHeight)));
    xform.Elements.Add("/Matrix", matrix);
    xform.Elements.Add("/Resources", pdfImage.Reference);
    xform.Elements.Add("/Length", new PdfInteger(0));

    // Now let's create the appearance stream, and add the XForm object
    var appearanceStream = new PdfDictionary(pdfDocument);
    appearanceStream.Elements.Add("/N", xform);
    cstamp.Elements.Add("/AP", appearanceStream);

    // Add to the page
    gfx.PdfPage.Annotations.Add(cstamp);

    pdfDocument.Save(@"c:\temp\1.pdf");
}

Edit 1: I have tried to add the Form as a Stream with the library, but I don't see an obvious way to do it. CreateStream seems to be an empty stream, and the xform variable has no reference at this point, so I can't attach the Reference to the /N. Maybe it's not even possible with this library?

byte[] buffer = new byte[10000];
appearanceStream.Stream = xform.CreateStream(buffer);
appearanceStream.Elements["/N"] = new PdfString(string.Format("q\n200 0 0 200 0 0 cm\n{0} Do\n Q", r));
1

There are 1 best solutions below

0
iPDFdev On

The xform needs to be a stream and you have to actually draw the image in this stream:

q
10 0 0 10 0 0 cm
/ImageID Do
Q

10 is the size of your xform (from your code) so the code above scales the image to cover the whole xform.
The /ImageID is the image identifier in the xform resources (I assume the r variable contains this value).

Please see the screenshot below that shows the COS objects structure in the PDF file for the custom stamp annotation. XO0 is the id of the image used as custom appearance. enter image description here