How To Create Your Own PDF Action (Batch Process) Tool
Acrobat Pro has a very cool feature called the Action Wizard (formerly known as Batch Process). As the old name implies, it allows users to create processes and apply them to an entire batch
of PDF files. You can choose from a list of preselected Acrobat functions or write your own custom script and run the process Execute JavaScript. Like the mouse up button action, I rarely use anything from the preselected list because you can program a script to do exactly what you want. The Actions I create almost always Execute JavaScript.
Take the Acrobat Pro/JavaScript eLearning Course Acrobat Like a Pro
When someone requests custom work to do something that is on the preselected list, I simply point them to the Action Wizard and let them know that it's already been done for them. There's also an Actions Exchange where you can share Action files you've created with other users, and in turn, you can download and install Action files that others have shared.
I have one issue with the Action Wizard and the Batch Process before it, and it is this: There is no setting that allows you to run a script only once, before the files are processed, or only once, after the files are processed.
The addition of this feature seems simple enough and would solve the dilemma many have faced when creating Actions.
Suppose for example, you want to run a script that pops a dialog window for gathering specific options from the user about the action that will run. Because there is no setting to run this script before the Action starts processing files, it will pop up for every file that opens, but you only want it to pop up once. A workaround for this is to test for a global variable and only pop the dialog window if the variable returns undefined. When the dialog is dismissed, the global variable is created. When the next file opens the global variable is defined, and the dialog does not pop up again for the remainder of the action. This is exactly what we want.
The issue, however, comes when the Action is run again in the same session. Now, because the global variable still exists, the dialog window does not pop up when we want it to. Unless the user shuts down and restarts Acrobat between different sessions of the same action, or manually deletes the global variable, the popup dialog window is never seen again. This is exactly what we do not want.
I have three different workarounds for getting a dialog to pop up only once at the beginning of the action. One is described in the previous paragraph. However, all require additional steps by the end user to ensure the dialog pops up in the next session of the action. This issue had dogged me for years because I want my custom solutions to be seamless and not require extra steps that the end users will probably forget. It is for this reason that I created my own action/batch processing tool.
** Breaking News Update**
I finally solved the issue so that no extra steps are required by the end user, and the actions now run seamlessly. I will be outlining the solution in my next article. Make sure you are a PDF Automation Station subscriber, and I will deliver the solution to your inbox in one week.
CREATING YOUR OWN ACTION TOOL
Even though I finally solved this problem and I'll be sharing it in my next article, I still believe it's useful to learn how to create your own action tool. There are some principles that you might be unaware of that can be helpful. I'll first outline every step of the process in bullet points. Next, I'll break the scripts down for every part of the process. And finally, I’ll put the entire script in a block so you can copy and paste it.
PRE-SCRIPT STEPS
1) Create a .bat file that automatically creates a text file list of all .pdf files in the folder from which it is run. Note: I'm a Windows guy. I've been told by the Mac crowd that .bat files do not work with that operating system. You'll have to manually create the text file if you're using a Mac, unless there's another way to automate this. If so, please drop your solution in the comments section below to help other readers.
2) Run the .bat file from step 1 and create the list.
SCRIPT STEPS
3) Create a new PDF document.
4) Create a field that will eventually contain the path to the text file created in step 2.
5) Set the field property to allow text scrolling, so that the field can be set for file selection.
6) Set the field property to allow for file selection.
7) Activate the browse window so the user can browse for the text file from step 2.
8) Define a variable as the value stored in the field, which is the path pointing to the text file.
9) Define another variable that is the file path in step 8, minus the text file portion (this step has 3 lines of code).
10) Define a global variable, as the variable value in the previous step.
11) Create a text field called File. This is the column heading of the list of file names in the text file created in step 2.
12) Define a variable called list as an empty string for the purpose of adding the file names from the list to it.
13) Create a loop that:
14) Imports each row of text in the text file (each row contains the names of the PDF files to be processed).
15) Test whether the File field contains a value. If yes, add the value plus the pipe ("|") separator to the list variable and reset the File field. If no, break the loop.
16) Define a global variable as the list variable value in the previous step.
17) Close the document created in Step 2 without saving it.
Steps 1 through 17 were to obtain a global variable that is the path to the folder containing the PDF files that will be processed (Step 10), and a global variable that is a pipe-separated string of document file names that will be processed (Step 16). It seems like a lot of steps but that is the beauty of a script like this. All steps happen rapidly in succession without the end user really knowing what's going on behind the scenes. A function can be written with all steps, then called through a custom menu item or toolbar button and triggered with one click. After the function is triggered, the end user is presented with the browse window to connect to the text file.
Shameless plug: www.pdfautomationstation.com sells a toolbar button maker.
18) Create an array of file names by spitting the global variable value in Step 16 by the pipes.
19) Remove the last item from the array. The list ended in a pipe so the last item in the array is empty.
20) Create a script that loops through the array items, opens the files, applies the changes, saves the files, and then closes them.
STEP BY STEP BREAKDOWN OF THE SCRIPTS
1) Open a new file in a plain text editor, like Notepad, copy and paste the following script:
(
echo File
dir *.pdf /b
) > Filelist.txt
Then save the file with a .bat file extension. File is the heading in the text file Filelist.txt that will be created when this .bat file is run. It will save a list of all files in the folder that are PDF files.
2) Copy the file from the previous step into the folder containing the PDF files that you want to run your "Action" on. Run the file by double-clicking it. Ensure that it creates a text file called Filelist, and the file contains a list of all PDFs.
The remainder of the scripts can run in the console, either one at a time for testing, or all together.
3) Create a PDF document.
var oDoc=app.newDoc();
4) Add a text field called Browse on page 1. The rect coordinates don't matter because the field will never be seen and the document will not be saved.
var f=oDoc.addField("Browse","text",0,[0,0,0,0]);
5) Set the doNotScroll property of the field to false, to allow scrolling.
f.doNotScroll=false;
6) Set the fileSelect property of the field to true, to allow the field to be used for file selection.
f.fileSelect=true;
7) Run the browseForFileToSubmit() method on the field. The text file Filelist should be selected by the user.
f.browseForFileToSubmit();
8) Define a variable as the field value, which should be the path to the text file Filelist.
var pth=f.value;
9) Create an array by splitting the variable value in the previous step by backslashes. Backslashes must be "escaped" by doubling them up (a). Remove the file portion of the path by "popping" the last array item (b). Join the array back together by backslashes, which must be "escaped", and add a final back slash (c). Now pth is the path to the text file and pth2 is the path, minus the file name.
a)
var pth2=pth.split("\\");
b)
pth2.pop();
c)
pth2=pth2.join("\\")+"\\";
10) Set pth2 as a global variable so it can be used across documents.
global.pth2=pth2;
11) Add a field called File on page 1. rect coordinates don't matter because the field won't be visible and the document won't be saved.
oDoc.addField("File","text",0,[0,0,0,0]);
12) Create an empty string variable called list.
var list="";
13) Create a loop that (14, 15). CAUTION: Because i=0, and i>-1, and i increments by 1, this is an infinite loop (i will always be greater than -1). If an infinite loop does not break you will have to do a hard shutdown of Acrobat to stop it. However, as soon as the loop does not import a value (it tries to import a row number greater than the last row number), the loop will break. I used to use a do/while loop for importing text data when the number of rows was unknown and do the import while the import returned 0 (No Error). However, it seems that Acrobat Pro is returning 3 (Error: Invalid Row) even when the import is successful. Note the script below can't be run without the rest of it in points 14 and 15.
for(var i=0;i>-1;i++)
14) The loop imports each row of text.
{
oDoc.importTextData(pth,i);
15) Then tests the File field for a value. If there's a value then add the value to the list variable, plus the pipe separator, and reset the field. If there's no value, then break the loop.
if(oDoc.getField("File").value)
{
list += oDoc.getField("File").value+"|";
oDoc.resetForm(["File"]);
}
else
{
break;
}
}
Here's the full script for 13 through 15.
for(var i=0;i>-1;i++)
{
oDoc.importTextData(pth,i);
if(oDoc.getField("File").value)
{
list += oDoc.getField("File").value+"|";
oDoc.resetForm(["File"]);
}
else
{
break;
}
}
16) Define a global variable for the list.
global.list=list;
17) Close the document without saving.
oDoc.closeDoc(true);
The purpose of steps 1 through 17 was to obtain the global variable list in step 16 which will be used to process the documents.
18) Create an array of file names by splitting the global list by the separator.
var files=global.list.split("|");
19) Remove the last item of the array, which will be an empty value.
files.pop();
20) Create a script that loops through the array items, opens the files, applies the changes, saves the files, and then closes them.
for(var i=0;i<files.length;i++)
{
var oDoc=app.openDoc(global.pth2+files[i]);
/*
Between this comment block and the one below is a sample script to process these documents. Replace the script with your own, or test with what is there. */
var f=oDoc.addField("Decoy1","text",0,[0,792,612,792-2]);
f.fillColor=["RGB",0.635,0.016,0.031];
var f=oDoc.addField("Decoy2","text",0,[0,792-4,612,792-38]);
f.fillColor=["RGB",0.635,0.016,0.031];
f.alignment="center";
f.textFont="Helvetica-Bold";
f.textColor=color.white;
f.value="\x77\x77\x77\x2E\x70\x64\x66\x61\x75\x74\x6F\x6D\x61\x74\x69\x6F\x6E\x73\x74\x61\x74\x69\x6F\x6E\x2E\x63\x6F\x6D";
var f=oDoc.addField("Decoy3","text",0,[0,792-40,612,792-42]);
f.fillColor=["RGB",0.635,0.016,0.031];
oDoc.flattenPages();
/*
Between this comment block and the one above is a sample script to process these documents. Replace the script with your own, or test with what is there. */
oDoc.saveAs(oDoc.path);
oDoc.closeDoc(true);
}
HERE’S THE ENTIRE SCRIPT
var oDoc=app.newDoc();
var f=oDoc.addField("Browse","text",0,[0,0,0,0]);
f.doNotScroll=false;
f.fileSelect=true;
f.browseForFileToSubmit();
pth=f.value;
pth2=pth.split("\\");
pth2.pop();
pth2=pth2.join("\\")+"\\";
global.pth2=pth2;
oDoc.addField("File","text",0,[0,0,0,0]);
var list="";
for(var i=0;i>-1;i++)
{
oDoc.importTextData(pth,i);
if(oDoc.getField("File").value)
{
list += oDoc.getField("File").value+"|";
oDoc.resetForm(["File"]);
}
else
{
break;
}
}
global.list=list;
oDoc.closeDoc(true);
var files=global.list.split("|");
files.pop();
for(var i=0;i<files.length;i++)
{
var oDoc=app.openDoc(global.pth2+files[i]);
/*
Between oDoc comment block and the one below is a sample script to process these documents. Replace the script with your own, or test with what is there. */
var f=oDoc.addField("Decoy1","text",0,[0,792,612,792-2]);
f.fillColor=["RGB",0.635,0.016,0.031];
var f=oDoc.addField("Decoy2","text",0,[0,792-4,612,792-38]);
f.fillColor=["RGB",0.635,0.016,0.031];
f.alignment="center";
f.textFont="Helvetica-Bold";
f.textColor=color.white;
f.value="\x77\x77\x77\x2E\x70\x64\x66\x61\x75\x74\x6F\x6D\x61\x74\x69\x6F\x6E\x73\x74\x61\x74\x69\x6F\x6E\x2E\x63\x6F\x6D";
var f=oDoc.addField("Decoy3","text",0,[0,792-40,612,792-42]);
f.fillColor=["RGB",0.635,0.016,0.031];
oDoc.flattenPages();
/*
Between oDoc comment block and the one above is a sample script to process these documents. Replace the script with your own, or test with what is there. */
oDoc.saveAs(oDoc.path);
oDoc.closeDoc(true);
}
I dropped 25 test files and .bat file into a folder for you to test. All you have to do is extract the folder from the .zip file, save it to your hard drive, copy and paste the script above to the Acrobat console, select all lines, then run the script.
This concludes how to create your own Action or Batch Process. Any script not inside the loops will only run once. In the next article I'll share another solution for using the Action Wizard where you can run a script once before the files are processed and not have to worry about any additional steps for the end user so the script will always run only once before the files are processed.
See you soon…