Flatten PDF annotations

Q: Thanks for your response. Can you point me to an example for use
annot.GetAppearance(), ElementReader, and ElementWriter for this
purpose? Or can you tell me where this is covered in the

A: As a starting point you may want to take a look at ElementReader,
ElementReaderAdv, and Annotation sample projects:

You can merge annotations by copying the content from the annotation
appearance stream to the target page. Something along the following

// Assuming you are using C# …
int num_annots = page.GetNumAnnots();
for (int i=0; i<num_annots; ++i) {
Annot annot = page.GetAnnot(i);
if (annot.GetType() == Annot.Type.e_Stamp) {
Obj app_stm = annot.GetAppearance();
if (app_stm != null) {
ElementReader reader = new ElementReader();

ElementWriter writer = new ElementWriter();

Element element;
while ((element = reader.Next()) != null) {
//… You may also need to recursively process form
// ProcessElement() in ElementReader and ImageExtract samples
for examples

After merging all annotations with the page, you can remove the
annotation using page.AnnotRemove(annot).

for (i=0; i<num_annots; ++i) {
Annot annot = page.GetAnnot(i);

Q: Thanks for your help. We did download the evaluation version and
testing the functionality. It seems to work great, but we noticed
that when
graphic stamps are flattened, they change location on the page. All
types of notations work fine.

I am attaching before & after examples. You can see that the Reviewed
has been moved.

Is there a way to avoid this?

A: We did some testing with PDFNet and the results are as expected. As
a result we suspect that the problem is due to API usage or special
case omissions. The following code is taken from another sample
project which works fine with the file you sent (it is using C++
however it should be fairly similar to C#).

if (Obj* annots = page.GetAnnots()) {
int ann_sz = int(annots->Size());
for (int i=0; i<ann_sz; ++i)
Annot annot(annots->GetAt(i));
Annot::Type annot_type = annot.GetType();

if (annot.GetFlag(Annot::e_hidden)) {
continue; // skip over hidden annotations

if (is_printing) {
if (!annot.GetFlag(Annot::e_print)) continue;
else { // screen preview
if (annot.GetFlag(Annot::e_no_view)) continue;

PDF::Rect ann_rect(annot.GetRect()); // Get annotation rectangle

// Check if the annotation is in the clip region.
PDF::Rect tmp;
if (!tmp.IntersectRect(ann_rect, clip)) {

SDF::Obj* form_ap = annot.GetAppearance();
if (form_ap)
PDF::Rect bbox(ann_rect);
if (Obj bbox_obj = form_ap->FindObj(“BBox”)) {
bbox = PDF::Rect(bbox_obj);

Common::Matrix2D frm_mtx(1, 0, 0, 1, 0, 0);
if (SDF::Obj* arr = form_ap->FindObj(“Matrix”)) {
frm_mtx.Set(arr->GetAt(0)->GetNumber(), arr->GetAt(1)-

arr->GetAt(2)->GetNumber(), arr->GetAt(3)->GetNumber(),
arr->GetAt(4)->GetNumber(), arr->GetAt(5)->GetNumber());

// Transform BBox using Forms Matrix
double p1x = bbox.x1, p1y = bbox.y1;
double p2x = bbox.x2, p2y = bbox.y1;
double p3x = bbox.x2, p3y = bbox.y2;
double p4x = bbox.x1, p4y = bbox.y2;

frm_mtx.Mult(p1x, p1y); frm_mtx.Mult(p2x, p2y);
frm_mtx.Mult(p3x, p3y); frm_mtx.Mult(p4x, p4y);

double min_x = Min(Min(Min(p1x, p2x), p3x), p4x);
double min_y = Min(Min(Min(p1y, p2y), p3y), p4y);
double max_x = Max(Max(Max(p1x, p2x), p3x), p4x);
double max_y = Max(Max(Max(p1y, p2y), p3y), p4y);
double frm_width = max_x - min_x;
double frm_height = max_y - min_y;
if (!frm_width || !frm_height) {

Common::Matrix2D transf(mtx);
transf *= Common::Matrix2D(ann_rect.Width()/frm_width, 0, 0,
ann_rect.Height()/frm_height, ann_rect.x1, ann_rect.y1);
transf *= Common::Matrix2D(1, 0, 0, 1, -min_x, -min_y); //
transf *= frm_mtx;

m_reader.Begin(form_ap, 0, 0);
CopyAnnot(despage, m_reader, transf);
else { // The annotation is missing the appearance.