Thom Parker February 18, 2010
Being able to save a file to disk is a critical activity for Acrobat workflow automation, and fortunately, there are a couple ways to do this from a script. This article presents scripts for not only saving a PDF file to disk, but also for saving the PDF to different formats, such as an image file, MS Word, text and even HTML.
This is a common form feature requested in the forums. Let me start right off by saying that it cannot be done. Or, to be more specific, it can only be done under very special circumstances. In the Acrobat/Reader environment, saving a PDF to disk is a protected operation. It’s a security issue. Users would not be very happy if random PDFs downloaded from the internet could silently save themselves to disk. The user has to know what’s going on. They have to either explicitly save the file using the "File > Save…" menu item, or implicitly allow the save through a trust mechanism.
Trust mechanisms are useful and appropriate in a small or closed environment, such as an office. They are not suitable for widely distributed files. There are three main trust mechanisms; batch sequences, trusted functions and digital signatures. The first two are useful for workflow automation. The last one, digital signatures, is most useful for business documents, such as contracts, that are passed back and forth between businesses or within an office. The point is at least one of these trust mechanisms must be employed in order for a script to be able to silently save a PDF to disk.
Another restriction is that even if a trust mechanism is in place, this function will only work in Reader if the PDF in question is Reader Enabled for Save Rights.
The primary use of saving a PDF through scripting is to support workflow automation. In the simplest case, the automation script only performs a single function, saving the current file to a specific folder. This functionality could be implemented with a folder level script that adds a new button to the Acrobat toolbar. The user simply presses the toolbar button and the file is saved, they don’t even have to know where the folder is. The script seems simple, and it is, but it saves the user from having to search the file system for the correct save location. The script saves time, hides the process details, makes training office workers simpler, and of course, the Save operation is a normal part of any larger workflow process.
There are two ways to save a PDF from a script. The simplest methodology is to use the "Save" menu item.
app.execMenuItem("Save");
This code saves the currently displayed PDF file in exactly the same way as when the users selects the "File > Save…" menu item. It works great when the automation script is operating on the current document. However, an automation script could be dealing with several documents at the same time. To handle this situation, the Save menu item can be applied to a specific PDF with this code:
var oMyDoc = <… PDF being operated on …> app.execMenuItem("Save", oMyDoc);
Saving to a different file name, folder, and format
The "doc.saveAs()" function is much more general-purpose than executing the Save menu item. For example, this code saves the current PDF to a temporary folder using a temporary file name. This code can be used in a process where a PDF is used as an intermediate file and was not going to be part of the final product:
this.saveAs("/c/temp/temp.pdf");
The code above uses a hard-coded path for saving the file. Notice the format of the path. In order to deal with cross-platform issues, Acrobat has a file path specification called the Device Independent File Path Format. If a script does not use this path format, the "doc.saveAs()" function will not work.
This code saves the file to the same file name, but to a new location:
this.saveAs("/c/MyDocs/" + this.documentFile Name);
This code saves the file to a new name, but in the same location:
// Split Path into an array so it is easy to work with var aMyPath = this.path.split("/");
// Remove old file name aMyPath.pop();
// Add new file name aMyPaty.push("NewFileName.pdf");
// Put path back together and save this.saveAs(aMyPath.join("/"));
When saving a file, it’s very important to include the whole path. The "saveAs()" function does not automatically use the path to the current file as a base.
Sometimes you want the save to automatically overwrite an existing file with the same name, which is the case in the first example where a temporary file is saved. There are also times when you’ll want to make sure a file is not automatically overwritten. If there is a conflict, you’ll want to warn the user and give them an opportunity to cancel the operation. For this situation, use the "bPromptToOverwrite" input as shown in the code below:
this.saveAs({cPath:cMyPath, bPromptToOverwrite:true});
The format of this function call is a little different. Notice the use of the curly braces, "{}", and that the input parameter names are spelled out. This format allows us to specify only the input parameters needed for the operation.
Converting a PDF to a different file format
The "saveAs()" function also has parameters for converting the PDF to a different file format. These are the same formats listed in the SaveAs dialog that’s displayed when the user selects the "File > Save As…" menu item. There are slight changes in which formats are available in different versions of Acrobat. The full listing of available formats can be found by running the following code in the Console Window:
app.fromPDFConverters.join("\n")
To convert all pages of the current PDF into separate JPEG files, use this code:
this.saveAs("/c/temp/test.jpg","com.adobe.acrobat.jpeg");
The second input parameter is the conversion ID taken from the "app.fromPDFConverters" list. The file name and path must be included, and the file name extension must match the file conversion. In this example, the pages of the PDF are converted into individual JPEG files. To do this, Acrobat appends "_Page_#" to the file name, so if the PDF had three pages, the file names would be:
test_Page_1.jpg
test_Page_2.jpg
test_Page_3.jpg
This naming convention is the same for all formats where each page is converted into an individual file.
This code converts the PDF into a single MS Word file:
this.saveAs("/c/temp/test.doc","com.adobe.acrobat.doc");
Unfortunately, converting PDF files into formatted, word-processing files does not always work very well because Acrobat doesn’t always know how to convert the PDF page formatting into correct structure in the destination file. So be careful with this one.
The cleanest conversion is into PostScript:
this.saveAs("/c/temp/test.ps"," com.adobe.acrobat.ps");
PostScript is a vector-based printing format closely related to PDF. I often use this conversion to completely flatten and remove all PDF features from a document. Acrobat can easily convert PostScript back into a clean PDF, so this is a perfect technique to use for converting LiveCycle forms into a flat, archival PDF.
Creating a custom save function
Remember, in order to use the "SaveAs" function, it has to be run from a privileged context. In most situations, this will mean creating a folder-level trusted function. The following code defines a function that performs only the save operation. This is a full working example. However, it could also be used as a template for a larger automation script.
var mySaveAs = app.trustedFunction( function(oDoc,cPath,cFlName)
{
app.beginPriv();
// Ensure path has trailing "/" cPath = cPath.replace(/([^\/])$/, "$1/");
try{
oDoc.saveAs(cPath + cFlName);
}catch(e){
app.alert("Error During Save");
}
app.endPriv();
});
The inputs to this function provide the document object, path and file name. All three are important for creating a generic function for saving a file. Inside the folder-level function the calling context is unknown. The keyword "this" may or may not be the current document. So it is very important to include the document object, "oDoc," even if the function is meant to be used on the current PDF.
If anything goes wrong with the save-- for example if any of the input parameters are wrong-- then the "saveAs" function will throw an exception. For this reason, it is encapsulated in a try/catch block. The try/catch block is to help the form or automation script developer debug their code. The user should never see the alert box, because the code calling this function should only pass in good parameters. But if there is a problem, you’ll know about it.
Once created, this folder-level function can be called from anywhere in the Acrobat JavaScript context, including from a script inside a PDF. For example, here’s a form- button script for saving the current file back to itself using this function:
// First make sure the function exists
if(typeof(mySaveAs) == "function") {
// Now split out the pure "Path" portion of file path
// for calling the function
var nLast = this.path.lastIndexOf("/");
var cPath = this.path.slice(0,nLast); mySaveAs(this,cPath, this.documentFileName);
} else {
app.alert("Missing Save Function\n" + "Please contact forms administrator");
}
This folder-level function will also work in Adobe Reader, but to do so, the PDF must be Reader Enabled with Save Rights. See the Scripting for Adobe Reader article for more information, and be sure to read the other articles linked above. They provide important supporting information and examples, especially the Device Independent File Path Format article.
Topics: Acrobat 9, JavaScript
0 comments
Leave a reply:
Commenting is not available in this channel entry.