Adding Javascript to PDF files generated with Adobe Livecycle Designer

Q: I would like to programatically insert Javascript in the click
event of a checkbox of a pdf file generated with Adobe Livecycle
Designer 8.2. In addition I would like to add a string variable
(which creates a Variables node) followed by

inserting a script object under the created Variable node.

This allows me to create javascript functions, global variables for
those functions, and allows me to reference these

functions from widget actions.

Can PDFNet do this, or at least some of this?

I tried to use your example snipit of adding a javascript \'mouse up\'
action trigger event on a checkbox widget in a pdf

generated by Adobe Livecycle Designer 8.2. This did not create the
app.alert when the checkbox was checked in Preview Mode.

The script was also not found in the script editor of Livecycle
Designer.

The code I used:

PDFNet.Initialize();
doc = new PDFDoc(@\"C:\\a\\PdfScript\\PdfScript\\test.pdf\");
doc.InitSecurityHandler();

PageIterator itr = doc.GetPageIterator(); for (; itr.HasNext();
itr.Next()) {
    Page page = itr.Current();
    int pageNumber = page.GetIndex();

    int num_annots = page.GetNumAnnots();
    for (int i = 0; i < num_annots; ++i)
    {
        Annot annot = page.GetAnnot(i);
        if (annot.IsValid() == false) continue;
        if (annot.GetType() == Annot.Type.e_Widget)
        {

            Obj aa_dict = annot.GetSDFObj().PutDict(\"AA\");

            Obj alert_js = doc.CreateIndirectDict();
            alert_js.PutName(\"S\", \"JavaScript\");
            alert_js.PutString(\"JS\", \"app.alert(\' Hello World! \');
\");

            // Associate this JS action with \'On Mouse Up\' event.
            aa_dict.Put(\"U\", alert_js);

        }

    }
}

doc.Save(@\"C:\\a\\PdfScript\\PdfScript\\test_out.pdf\",
SDFDoc.SaveOptions.e_linearized); doc.Close();

Thanks for your help.
--------------
A: It is possible that XFA information is not in sync with AcroForms.

In this case removing XFA from AcroForms dictionary or sync XFA with
AcroForms. For example:

Obj acro_form = pdfdoc.GetAcroForm();
if (acro_form != null)
{
  // This PDF document contains forms...
  if (acro_form.FindObj("XFA") != null) {
      // This PDF document contains XFA forms...
      acro_form.Erase("XFA");
  }
}

Q: I used your snipet to erase the XFA dictionary before my code and
the resulting output worked when opened in Acrobat Professional, but
would no longer open directly in LiveCycle.

Can I add javascript without deleting the XFA info?

Is there a way to extract the XML info out of the pdf, manipulate it,
then insert it back in?

Basically the question is:

"How can I replace the xml template from in an xfa PDF?"

I can extract the xml template from an xfa document using the
following:

      Obj obj = acro_form.FindObj("XFA");

      byte[] buff = new byte[4000];

      pdftron.Filters.Filter filter = obj.GetAt(5).GetDecodedStream();
      pdftron.Filters.FilterReader fr = new
pdftron.Filters.FilterReader(filter);
      fr.Read(buff);
      string str = Encoding.ASCII.GetString(buff);

      str = str.Replace("clicky clicky", "clicky clank clicky clank");

How do I get this modified string back into the document so I can save
it to a file?

This would be extreemly usefull, since I could manipulate the
underlying xml directly.
-------------
A: You can create a new stream from your string object (i.e. XFA
replacement string) using:

Obj new_xmp_stm = doc.CreateIndirectStream(new
System.Text.UTF8Encoding().GetBytes(str));

You can then either put this stream into the appropriate place in XFA
array:
  acro_form.Get("XFA").Value().PutAt(5, new_xmp_stm);

or swap it with the existing stream:

  doc.GetSDFDoc().Swap(new_xmp_stm.GetObjNum(),
    acro_form.Get("XFA").Value().GetAt(5).GetObjNum());

Q: My ultimate goal is to read the xml template from a pdf generated
with Adobe LiveCycle Designer, modify the xml and then replace the
modified xml back into the file. So far, I can get the xml, but can't
completely replace the stream succesfully. Thanks to your response I
can replace the stream but the new saved file is no longer recognized
by LiveCycle Designer. Upon further investigation, it apears that the
stream is replaced correctly, but the information that tells how the
stream is encoded in the PDF is lost. The following code illustrates
the point. Notice that before the stream swap the raw and decoded
first byte of the buffer values are different, but after the swap they
are the same. The output is: Pre Raw: 72, Pre Decoded 10, Post Raw:
72, Post Decoded: 72

How do I replace the stream while keeping the stream encode info in
the pdf file? What is the encoding\\decoding used (I could not find
it using GetDecodeName or GetAttachedFilter)? How do I get from Obj
new_xmp_stm = doc.CreateIndirectStream(new System.Text.UTF8Encoding
().GetBytes(str)); to the proper encoded stream to swap with the the
origional pdf file stream AND still allow the file to know what
encoding was used?

PDFDoc doc = new PDFDoc(@\"C:\\a\\PdfScript\\PdfScript\\test.pdf\");
doc.InitSecurityHandler();

Obj acro_form = doc.GetAcroForm();
if (acro_form != null)
{
  // This PDF document contains forms...
  if (acro_form.FindObj(\"XFA\") != null)
  {
      // This PDF document contains XFA forms...
      Obj obj = acro_form.FindObj(\"XFA\");

      byte[] buff = new byte[4000];
      byte byteRawPre, byteDecodePre, byteRawPost, byteDecodePost;

      pdftron.Filters.Filter filter;
      pdftron.Filters.FilterReader fr;

      filter = obj.GetAt(5).GetDecodedStream();
      fr = new pdftron.Filters.FilterReader(filter);
      fr.Read(buff);
      byteDecodePre = buff[0];

      filter = obj.GetAt(5).GetRawStream(false);
      fr = new pdftron.Filters.FilterReader(filter);
      fr.Read(buff);
      byteRawPre = buff[0];

      //replace stream with raw stream
      Obj new_xmp_stm = doc.CreateIndirectStream(buff);//raw stream
      doc.GetSDFDoc().Swap(new_xmp_stm.GetObjNum(), acro_form.Get(\"XFA
\").Value().GetAt(5).GetObjNum());

      filter = obj.GetAt(5).GetDecodedStream();
      fr = new pdftron.Filters.FilterReader(filter);
      fr.Read(buff);
      byteDecodePost = buff[0];

      filter = obj.GetAt(5).GetRawStream(false);
      fr = new pdftron.Filters.FilterReader(filter);
      fr.Read(buff);
      byteRawPost = buff[0];

      string resStr = string.Format(
          \"Pre Raw: {0}, Pre Decoded {1}, Post Raw: {2}, Post
Decoded: {3}\",
          byteRawPre.ToString(), byteDecodePre.ToString(),
byteRawPost.ToString(), byteDecodePost.ToString());

      MessageBox.Show(resStr);

      doc.Save(@\"C:\\a\\PdfScript\\PdfScript\\test_out.pdf\",
SDFDoc.SaveOptions.e_linearized);
      doc.Close();

  }
}

//Note: You sent me an alternative: acro_form.Get(\"XFA\").Value
().PutAt(5, new_xmp_stm);
//But there is no PutAt in your library

Thanks for your help. I finally figured this out with the help of
your response.

The 2 missing pieces are the stream must be compressed, and the byte[]
must be the exact size of the info (no trailing zeros) before using
Swap.

That issue is now resolved. I can now manipulate and save xfa files
through the underlying xml.