How to edit & replace layers/header/footer/watermark added to existing PDFs?

Q:

Our application is using PDFNet SDK to add new content to existing PDF
documents (e.g. header/footer/watermark).

We would like to extend the application to allow the user to replace
text and other contents in the content layers added my our
application. How can we do this using PDFNet?

Also how can we rotate new content?
----------

A:
To replace a header/footer or any layer that your application may add
to an existing PDF document you can use the following technique:

Instead of adding PDF elements directly to a page, create a form
XObject and then place the form on the page.

For example:

static Obj CreateMyPDFHeader(PDFDoc doc, ... params ...) {
  ElementBuilder build = new ElementBuilder();
  ElementWriter writer = new ElementWriter();
  writer.Begin(doc);

  Image img = Image.Create(doc, "my_logo.png");
  int w=img.GetImageWidth(), h=img.GetImageHeight();
  Element img_element = build.CreateImage(img, 0, 0, w, h);
  writer.WritePlacedElement(img_element);
  // You can also add text, paths, etc ...
  // see ElementBuilder for more examples of how to create new
content.

   Obj form = writer.End();
   // Set the bounding box
   form.Put("BBox", Rect.CreateSDFRect(0, 0, w, h));
   form.Put("Subtype", Obj.CreateName("Form"));

   // Add custom tag.. identifying this form as being a
   // header and created by my company...
   form.Put("__MyPrivate_Header_Tag", Obj.CreateBool());

   return form;
}

You can add the custom header to an existing page as follows:

Page page = ...grab an existing page...
ElementBuilder builder = new ElementBuilder();
ElementWriter writer = new ElementWriter();
writer.Begin(page); // begin writing to this page
Element element = builder.CreateForm(CreateMyPDFHeader());

double header_width/height = ... pos_x/y =...
element.GetGState().SetTransform(header_width, 0, 0, header_height,
pos_x, pos_y)
writer.WritePlacedElement(element);
writer.End();

When you application is editing existing page content (as illustrated
in ElementEdit sample project - http://www.pdftron.com/net/samplecode.html#ElementEdit)
you can search for elements of type e_form:

ElementReader reader = new ElementReader();
Reader.Begin(page);
while ((element = reader.Next()) != null) { // Read page contents
  if (element.GetType() == Element.Type.e_form) {
     Obj form = element.GetXObject();
     if (form.FindObj("__MyPrivate_Header_Tag") != null) {
        ... form is the header added by your application !!!
        ... You can replace the form with another form,
        ... delete it, etc...
     }
  }
}

How do we rotate new content.

You can specify rotation using SetTransform(Matrix2D) on any PDF
element.

For example, you could rotate the header in the above example as
follows:

// Create a transformation matrix for the form xobject:
double deg2rad = 3.1415926535 / 180.0;
pdftron.Common.Matrix2D mtx = new
pdftron.Common.Matrix2D.RotationMatrix( angle * deg2rad );
mtx.Scale(s1, s2);
mtx.Translate(x, y);
// ...
element.GetGState().SetTransform(mtx);

Q:

We wanted to know how to replace or Delete the form. We need some
sample code how to Replace or Delete the e_form which is created. We
know how to create header/footer using e_form, but we did not get
right method for Replace/Delete from SDK reference manual.
----
A:

There is no specific method to delete or replace an element. Instead
you would copy elements from one page to another. In the process you
can modify, omit/delete, insert new, or replace existing elements.

You may want to take a look at ElementEdit sample (http://
www.pdftron.com/net/samplecode.html#ElementEdit). This sample strips
all images from the page and changes text color to blue.

Q:

We have tried the suggested solution we got few things working such as
Rotation of Water mark, Replacing Header/Footer using e_form element.
The remaining issue is that we are getting an error when the document
is opened in Acrobat. Do you know what could be the problem?

string Filpath=@"input.pdf";
double horScale=1;
double verScale=1;
double horTrns=0;
double verTrns=0;
double fontSize=30;
string sText="Watermark Test";
float ha=0;
float va=0;
double rotation=0;
PDFDoc doc=null;
int h=2;
int v=2;

PDFNet.Initialize();
doc=new PDFDoc(Filpath);

if (!doc.InitSecurityHandler())
      {
            MessageBox.Show("Document authentication error", "PDFView
Error");
      }

Element element=null;
Element new_element=null;
ElementBuilder eb = new ElementBuilder();
ElementWriter writer = new ElementWriter();
PageIterator itr=doc.PageBegin();
PageIterator end = doc.PageEnd();
Page p=itr.Current();
double width=p.GetPageWidth();
double height=p.GetPageHeight();

writer.Begin(doc);
new_element = eb.CreateTextBegin(pdftron.PDF.Font.Create(doc,
pdftron.PDF.Font.StandardType1Font.e_times_roman), fontSize);
writer.WriteElement(new_element);
new_element = eb.CreateTextRun(sText);

ColorPt colrpt=new ColorPt((192.0/255.0),(192.0/255.0),(192.0/255.0));
GState gs = new_element.GetGState();
gs.SetFillColorSpace(ColorSpace.CreateDeviceRGB());
gs.SetFillColor(colrpt);

switch(v)
{
      case 0:
            verTrns=(height-(fontSize*verScale)-va);
            break;
      case 1:
            verTrns=va+8;
            break;
      case 2:
            verTrns=(height-(fontSize*verScale)-va)/2;
            break;
}

switch(h)
{
      case 0:
            horTrns=ha;
            break;
      case 1:
            horTrns=(width-(new_element.GetTextLength()*horScale)-ha);
            break;
      case 2:
            horTrns=(((width)-(new_element.GetTextLength()*horScale-
ha))/2);
            break;
}

double deg2rad = 3.1415926535 / 180.0;
Matrix2D mtx = new Matrix2D();
mtx = Matrix2D.RotationMatrix(-rotation * deg2rad );
mtx.Scale(horScale, verScale);
mtx.Translate(horTrns, verTrns);
new_element.GetGState().SetTransform(mtx);
writer.WriteElement(new_element);
writer.WriteElement(eb.CreateTextEnd());

Obj form = writer.End();
form.Put("Subtype", Obj.CreateName("Form"));
// <----- Add the following line to resolve the problem.
form.Put("BBox", Rect.CreateSDFRect(0, 0, p.GetPageWidth(),
p.GetPageHeight()));

form.Put("__MyPrivate_Header_Tag", Obj.CreateBool(true));
element=eb.CreateForm(form);

for (; itr!=end; itr.Next())
{
      writer.Begin(itr.Current());
      writer.WriteElement(new_element);
      writer.End();
}
doc.Save(@"mydoc.pdf",0);
----

A:

It seems that you forgot to include BBox entry in the form dictionary.
Adding the following line just before placing the form on the page
will solve the problem:
  form.Put("BBox", Rect.CreateSDFRect(0, 0, p.GetPageWidth(),
p.GetPageHeight()));

Q:

In ElementEdit sample you are considering only graphical elements
(i.e. page content) while copying to new page. What about the other
contents (like Annotation etc)? How to copy other parts of the page
besides page content?
----
A:

You can copy annotations from one page to another using the approach
described in the following KB article:

http://groups.google.com/group/pdfnet-sdk/browse_thread/thread/781ceb71975c1c6e
(or search for "merge annotations").

Another approach to page editing which is in some cases better than
editing the entire page is to replace the form object with a new
version. This approach is suggested in the following KB article:

Search for "How to replace raster images on a PDF page without
creating a new page".

This KB article talks about replacing raster images on existing PDF
pages, however the same approach applies to form XObjects.

In case you need to change form positioning and size in addition to
replacing the content, you can introduce another level of indirection.
In this case you would place another form XObject on the page which
would reference and position the content form XObject (e.g. page
footer/header).