Flatten PDF annotations

490 views
Skip to first unread message

Support

unread,
Dec 2, 2013, 5:01:56 PM12/2/13
to
Q:  I need to flatten annotations inside an existing PDF.  The
flattening must include both text and graphical annotations.  Can I
use PDFNet SDK to implement this feature?

---
A:  You can use PDFNet SDK (www.pdftron.com/net)  to flatten
annotations inside an existing PDF. As a starting point you may want
to take a look at the last code example in InteractiveForms sample
project (http://www.pdftron.com/net/samplecode.html#InteractiveForms).
The utility method pdfdoc.FlattenAnnotations(true) will flatten all form fields
that have appearance streams. If the PDF document does not contain
appearance streams you may need to call
pdfdoc.RefreshFieldAppearances() before flattening.

Using PDFNet API you can also implement flattening for other
annotation types besides widgets (e.g. markup, notes). In this case
you would use annot.GetAppearanceStream() and ElementReader to read
elements from the annotation and ElementWriter to merge the graphical
elements with the underlying page.

Support

unread,
Jun 23, 2008, 5:59:51 PM6/23/08
to PDFTron PDFNet SDK
Q: Thanks for your response. Can you point me to an example for use
of
annot.GetAppearance(), ElementReader, and ElementWriter for this
purpose? Or can you tell me where this is covered in the
documentation?

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

http://www.pdftron.com/net/samplecode.html#ElementReader
http://www.pdftron.com/net/samplecode.html#ElementReaderAdv
http://www.pdftron.com/net/samplecode.html#Annotation

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

// 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();
reader.Begin(app_stm);

ElementWriter writer = new ElementWriter();
writer.Begin(page);

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

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);
page.AnnotRemove(annot);
}

Support

unread,
Jun 27, 2008, 9:20:28 PM6/27/08
to PDFTron PDFNet SDK
Q: Thanks for your help. We did download the evaluation version and
are
testing the functionality. It seems to work great, but we noticed
that when
graphic stamps are flattened, they change location on the page. All
other
types of notations work fine.

I am attaching before & after examples. You can see that the Reviewed
stamp
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
ann_rect.Normalize();

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

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);
bbox.Normalize();
}

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)-
>GetNumber(),
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) {
continue;
}

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);
m_reader.End();
}
else { // The annotation is missing the appearance.
}
}
}
Reply all
Reply to author
Forward
0 new messages