Expand my Community achievements bar.

Including attachments from PDF in Save as Draft and Submit as XDP

Avatar

Former Community Member

When I wrote the first large process years ago, I ran into the problem of saving attachments that are in the PDF. If you ask the question, the response will be that you have to submit as PDF. That in turn has it's own other problems which when trying to solve, the answer given is "That is because you are submitting as a PDF". I have seen many ask the same thing I wanted: Submit as XDP, but include the attachments. This also includes that the built in Save Draft save them too.

The summary is this: Loop through the attachments in JavaScript, Base64 encode them and stick them into the DOM. On the server side, use a script step to loop through and create a map of document objects to pass to the next render.

The following needs to be called from a preSubmit event passing event.target. You'll need to add the nodes referenced to your schema.

function embedAttachments(oParent) {

    // Get the list of attachments

    var oDataObjects = oParent.dataObjects;

    //app.alert(oDataObjects);

   

    var rootNode = xfa.datasets.data.resolveNode("[Your Data Root Node]");

    var oAttachData = rootNode.nodes.namedItem("FileAttachments");

    var oldNodes = oAttachData.nodes;

   

    //wipe out empty nodes

    while (oldNodes.length != 0) {

      oldNodes.remove(oAttachData.nodes.item(0));

    }

    if (oDataObjects != null) {

      var count = oDataObjects.length;

     

      if (count > 0) {

          // Loop through all the attachments

          for (var i = 0; i < count; i++) {

            // Grab the next attachment

            var oFile = oParent.getDataObjectContents(oDataObjects[i].name);   

              // Get a new stream with the image encoded as base64

            var vEncodedStream = Net.streamEncode(oFile, "base64");

            // Get a string from the stream

            var sBase64 = util.stringFromStream(vEncodedStream);

              //console.println(sBase64);

       

              // Copy the data to the XML

            var oNewNode = xfa.datasets.createNode("dataGroup", "FileAttachment");

            oAttachData.nodes.append(oNewNode);

       

            var oName = xfa.datasets.createNode("dataValue", "FileName");

            var oDesc = xfa.datasets.createNode("dataValue", "FileDesc");

            var oData = xfa.datasets.createNode("dataValue", "Base64Data");

       

            oName.value = oDataObjects[i].path;

            oDesc.value = oDataObjects[i].description;

            oData.value = sBase64;

       

            oNewNode.nodes.append(oName);

            oNewNode.nodes.append(oDesc);

            oNewNode.nodes.append(oData);   

          }

      }

    }

}

It also needs to be called from ContainerFoundation_JS in the form bridge.

Add this variable:

var thisDoc = null;

Add this line of code at the top of RegisterMessageHandler:

    thisDoc = event.target;

Add this line of code to the top of getData function:

xfa.form.[Root Node].[Script Object].embedAttachments(thisDoc);

Here is the Java code to add to a script object. I put mine in a custom render.

import java.util.HashMap;

import java.util.*;

import org.w3c.dom.*;

import com.adobe.idp.Document;

import org.apache.commons.codec.binary.Base64;

int count = 0;

Map attachmentMap = new HashMap();

Node ndAttach = patExecContext.getProcessDataValue("/process_data/xmlData/Formdata/FileAttachments");

if (ndAttach != null) {

    NodeList children = ndAttach.getChildNodes();

    if (children != null) {

        count = children.getLength();

    }

}

for (int i = 1; i <= count; i++){

    String name = patExecContext.getProcessDataStringValue("/process_data/xmlData/Formdata/FileAttachments/FileAttachment[" + i + "]/FileName");

    String desc = patExecContext.getProcessDataStringValue("/process_data/xmlData/Formdata/FileAttachments/FileAttachment[" + i + "]/FileDesc");

    String b64Data = patExecContext.getProcessDataStringValue("/process_data/xmlData/Formdata/FileAttachments/FileAttachment[" + i + "]/Base64Data");

    if (b64Data != null && b64Data.length() != 0) {

        Document attDoc = new Document((new Base64()).decode(b64Data.getBytes()));

        attDoc.setAttribute("basename", name);

        attDoc.setAttribute("description", desc);

        attDoc.setAttribute("wsPermission", "1");

        attDoc.passivate();

        attachmentMap.put(name, attDoc);

    }

}

patExecContext.setProcessDataMapValue("/process_data/mapAttachments", attachmentMap);

After I wrote that, I realized there is a method to create a document from Base64. Since I can inspect the map during record and play back and see that the documents are stored Base64, I think I could speed up the process by changing to the other method. I am assuming it would prevent a decode then encode. This same technique might also be applied to annotations.

1 Reply

Avatar

Former Community Member

Revised Execute script. Server was running out of heap space with large attachments. Creating the Document objects as temp files instead of in memory solves that. I also added the part that wipes the Base64 Attachments out of the XML.

        int count = 0;
        Map attachmentMap = new HashMap();
        String name="";
        String b64Data="";
        File tempFile=null;
        FileOutputStream outStream=null;
        Document attDoc=null;
        int i=0;

        Node ndAttach = (Node) patExecContext.getProcessDataValue("/process_data/xmlData/Formdata/FileAttachments");
        NodeList children;
        Node childNode = null;
        if (ndAttach != null) {
            children = ndAttach.getChildNodes();
            if (children != null) {
                childNode = children.item(i);
                if (childNode instanceof Element) {
                    System.out.println("tag name: " + ((Element)childNode).getTagName());
                }
                count = children.getLength();
            }
        }
        for (i = 1; i <= count; i++){
            b64Data = patExecContext.getProcessDataStringValue("/process_data/xmlData/Formdata/FileAttachments/FileAttachment[" + i + "]/Base64Data");

            if (b64Data != null && b64Data.length() != 0) {
                name = patExecContext.getProcessDataStringValue("/process_data/xmlData/Formdata/FileAttachments/FileAttachment[" + i + "]/FileName");
                tempFile = File.createTempFile("Attachment", ".tmp");
                outStream = new FileOutputStream(tempFile);
                outStream.write(Base64.decodeBase64(b64Data.getBytes()));
                outStream.close();

                attDoc = new Document(tempFile, true);
                attDoc.setAttribute("basename", name);

                attDoc.setAttribute("description", patExecContext.getProcessDataStringValue("/process_data/xmlData/Formdata/FileAttachments/FileAttachment[" + i + "]/FileDesc"));
                attDoc.setAttribute("wsPermission", "1");
                attachmentMap.put(name, attDoc);
            }
        }

        patExecContext.setProcessDataMapValue("/process_data/mapAttachments", attachmentMap);

        while (ndAttach.hasChildNodes()) {
            ndAttach.removeChild(ndAttach.getLastChild());
        }