What Are You Interested In? Last Year’s Top Ten Pages

I am always interested in what article on my blog are the ones that remain popular over time. Here is last year’s top ten list:

# Page Name / Link Page Views (Percent)
1 Duplicate a Page in Adobe Acrobat 14.37%
2 “No Pages Selected To Print” Error 10.14%
3 Where are my Adobe Acrobat 9 Updates??? 8.70%
4 Home Page 7.71%
5 Modify Dynamic PDF Stamps in Acrobat 7.14%
6 Validating Field Contents 6.31%
7 Batch-Import Excel Data into PDF Forms 3.97%
8 Missing Characters After Merging or Inserting PDF Files? Here is a Potential Workaround 2.75%
9 Create Custom Commands in Adobe Acrobat Pro DC 2.70%
10 Acrobat DC is Here – You may want to wait with upgrading until you read this… 2.42%

Let’s take a look at the different pages and my take on the issues:

#1: Duplicate a Page in Adobe Acrobat

This is a surprise winner: I would not have thought that duplicating a page in Acrobat is such an important feature. For those who’ve read the page – or know how to do this already – it’s not very intuitive, so that may explain why people are searching for (and finding) this page.

#2: “No Pages Selected To Print” Error

No surprise here, based on my work on the (now defunct) AcrobatUsers.com and the Adobe Forums, I know that this problem has been a around for a few years without a fix or a good explanation about why it’s happening in form of a KB article from Adobe.

#3: Where are my Adobe Acrobat 9 Updates???

Adobe has fixed part of the problem by now providing a PDF file that explains what updates need to be applied in what order, but users are still struggling with finding the updates. The FTP server is just not as intuitive as clicking on a web link.

#4: Home Page

Almost 8% of visitors come in through the home page (or end up on the home page eventually). This is interesting – and good – for me because that’s where I advertise my services. I have to pay my mortgage and eat so that I can create all this free content. If you have any needs in the big world of PDF, please consider my consulting business as a shortcut to a working solution. And to those of you who have hired me: Thank you!

#5: Modify Dynamic PDF Stamps in Acrobat

This is an old one, but still valid and useful. Oftentimes it’s easier to just modify one of Acrobat’s own dynamic stamps to create something new, compared to starting from scratch. Stamps are a big part of my business, so there is no surprise that this has been a top 10 contender for a number of years.

#6: Validating Field Contents

A good form needs form field validation, and oftentimes that can only be done using JavaScript. One of these days I will write about using regular expressions in these validation scripts, which oftentimes makes things a lot easier.

#7: Batch-Import Excel Data into PDF Forms

This again is a big part of my business, and I know that a lot of people get “their feet wet” with the simple examples that I posted and then turn to me for help for more complex solutions. There is a lot more that can be done with importing (or exporting) form data than what I am able to describe in these simple examples.

#8: Missing Characters After Merging or Inserting PDF Files? Here is a Potential Workaround

I’ve spent a lot of time in debugging different missing character problems in Acrobat, and this is still the most straight forward way of fixing some of these issues. It does not work for all problems, but it’s a good first step in the debugging process.

#9: Create Custom Commands in Adobe Acrobat Pro DC

This is almost two years old, but custom commands are still one of my favorite new addition to Adobe Acrobat DC. Oftentimes It’s like Actions, but for one document, without the overhead of an Action, and they can be added to the toolbar. If you want to automate things in Acrobat, this is a good staring point.

#10: Acrobat DC is Here – You may want to wait with upgrading until you read this…

This is no longer an issue, and hopefully it will make room for other top 10 candidates next year.

Posted in Blogging, Misc | Leave a comment

Rotate PDF Fields in Adobe Acrobat Using JavaScript

Have you tried to rotate a field in a PDF form after it was created in Acrobat? If so, you may have scratched your head a bit.

Before we get to the how, let’s first talk about the why: When you have two different documents, one having a page rotation of 0 degrees, and the second one with a page rotation of 90 degrees (or, two different pages in the same document with different page rotations), and you copy a form field from one page to a page with a different page rotation, the form field will be rotated, and you will have to rotate it back in order to get the correct alignment and orientation. When you now try to rotate this field by setting it’s rotation property, you will very likely end up with something that looks nothing like what you expected (that is, if you’ve selected a rotation of 90 or 270 degrees, with 180 degrees things will look OK without having to do anything else).

Here is what happens when you do that:

The original form fields:

Screen shot showing differnet form field types (text box, button, dropdown and list control) in their unrotated state.

And the same after the fields are rotated by 90 degrees:

Screen shot showing the same form fields as before, but after a 90 degree rotation was applied, the content is rotated, but the fields are still in the same location as before, and have the same size. This means that the content is cut off.

It’s pretty obvious that there is something wrong here: The only thing that was rotated is the form field content, but the field’s location, width and height are the same as before. When we take a step back and look at how a rotated form field is placed in a form, that makes sense: We would first draw the outline of e.g. a text field, and that would very likely be taller than it’s wide:

Screen shot of a text field that is taller than it's wide

And, in a second step, you would then change the rotation of the field on it’s properties dialog:

Screen shot of a field properties dialog with the field rotation set to 90 degrees

This will then result in correctly aligned text in that text field. That is different when we have a text field that is meant for horizontal text, and then we rotate the field content as we’ve done in the first example. To make this work, we have to both rotate the field content (easily done by setting the rotation property) and we have to resize the field, which is a bit more complicated.

The first thing we have to decide is which corner of the field should stay where it is, which in turn identifies the three corners that need to change their location. In the following example, I skip this problem, it does add some complexity to the solution. For now, you would have to move the rotated form field into place manually – which is much simpler to do, compared with having to resize the field by hand and then setting the rotation flag. You can use this code as a custom command, or just run it in the JavaScript console. If you do run the code in the console, it’s very easy to run it four times and loop through the four different field rotations of 0, 90, 180 and 270 degrees.

for (var i = 0; i < this.numFields; i++) {
	var theName = this.getNthFieldName(i);
	var f = this.getField(theName);
	var rect = f.rect;
	var rot = f.rotation;

	var upperLeftX = rect[0];
	var upperLeftY = rect[1];
	var lowerRightX = rect[2];
	var lowerRightY = rect[3];

	rect[0] = upperLeftY;
	rect[1] = upperLeftX;
	rect[2] = lowerRightY;
	rect[3] = lowerRightX;

	f.rect = rect;
	rot = rot + 90;
	if (rot >= 360) {
		rot = rot - 360;
	}
	f.rotation = rot;
}

The snippet first gets the currently set rotation (which may not be 0 degrees), and then applies a 90 degree rotation to whatever it found. In addition to that, it also swaps coordinate components. This is where you would need to make an adjustment for keeping one of the field corners constant.

There are of course no limits in how you can improve this snippet. The number one priority would probably be to keep the rotated field in the same area as the original field. You can also add some logic to only rotate certain fields by checking the field names again e.g. an array of fields to rotate. You can filter by field type and e.g. only rotate text fields and dropdown controls.

Posted in Acrobat, JavaScript, PDF, Tutorial | 2 Comments

New Form Field Types in Acrobat DC: Image Field and Date Picker

This morning Adobe released an update for the “Continuous” track of Adobe Acrobat DC – this means that anybody with a subscription will be able to install this update and get new features.

There are a number of new and improved features, which you can look up in the release notes, or in the New Features Summary: https://helpx.adobe.com/acrobat/using/whats-new.html

For me, the two most attractive new features are two new field types when creating PDF forms:

Two new icons on the Acrobat toolbar when editing PDF forms

  • Date Picker
  • Image Field

Let’s first look at the Date Picker:

2017 01 10 12 18 28

When the “Add a Date Field” tool is selected, the user can add a new date field to a PDF form. The actual process is the same as for all the other field types, and once placed, the new field looks just like a text field. The reason for this is that the field is actually a text field, but with the format already set to one of the date options. Once a form with such a date field is filled out, the appearance of the field is now different from a regular text field:

Date field with a triangle indicator that more information will be available when clicked on

The field now shows a triangle on the right side that indicates that more functionality will be exposed once the user clicks on that triangle – and that’s how we display the date picker:

2017 01 10 11 48 51

The user can still just type a date, just like before, but the date field can now also be populated by using the date picker. There have been 3rd party date pickers for PDF forms in the past, but this is the first time that this feature is built right into Adobe Acrobat and the free Adobe Reader.

When a date field is placed using this new method, a new default field name will be used, but the user can of course change that to fit any naming convention used in a form.

What’s even more exciting is that you don’t have to change all your forms that ask for date input: The “Add a Date Field” tool is just a shortcut for adding a text field and then changing the field formatting to be of a date type. This means that any old form that uses text fields with date formatting will automatically display the date picker when opened in the latest version of Adobe Acrobat or the free Adobe Reader. Yay!

The image field is also a shortcut, but with something extra thrown in. In the past, you could create an image field by creating a button, setting some of the button’s appearance options, selecting to use a button icon, and then running a one line JavaScript to set the button icon to an image that the user could select. This is now all set automatically when the “Add an Image field” function is used:

Just like if we would do this manually, we can now see that this button uses this line of JavaScript:

event.target.buttonImportIcon();

Which means that when the user clicks on that button, Field.buttonImportIcon() method will be executed and because it’s called without parameters, it will prompt the user to select a file.

Here is the really cool thing about this last round of Acrobat/Reader updates: In the past, when a user tried to select a file in the free Adobe Reader, only PDF files could be selected, whereas with Adobe Acrobat, PDF files and all supported image formats could be selected. In the new version of Adobe Reader, we can now also select image files. This makes things a lot easier for users of the free Reader – they no longer have to figure out how to convert an image to a PDF file. The image can now be selected and stored in the PDF file without any additional work.

If you have not yet updated your Adobe Acrobat DC subscription version or your Adobe Reader DC, select the “Check for Updates” function in the application’s “Help” menu so that you can take advantage of this new functionality.

Posted in Uncategorized | Tagged , , , , | 4 Comments

Learning to Program JavaScript for Adobe Acrobat

This is a bit longer than usual, so let me add a table of contents here that allows you to jump straight to the section you are interested in.

JavaScript in Acrobat

Programming JavaScript for Acrobat is simple: Just use the JavaScript core language, avoid any browser specific extensions to the JavaScript language and become familiar with the Acrobat JavaScript API…

… that is if you are already a JavaScript expert, and know where exactly the boundary between the core language and these browser specific extensions are.

So let’s take a step back and see how one can learn to program in JavaScript for Acrobat from scratch.

What is JavaScript?

Back in the early days of the World Wide Web, JavaScript was created in 1995 as an extension language for the Netscape browser. If you want to learn more about it’s history, feel free to explore the JavaScript Wikipedia page.

Since then, it came a long way, and left it’s browser-only heritage behind. It is now available for a number of different environments. Adobe uses it as it’s “ExtendScript” to automate different Creative Cloud applications (Photoshop, InDesign, Illustrator, …), but also in Adobe Acrobat (and that’s very likely why you are here, reading this blog post). Any JavaScript implementation consists of two parts:

  • The JavaScript “core” language
  • Application specific extensions

All JavaScript implementations have the first part in common, and as long as we ignore changes over time in that core language part, any script written with just these core language elements should run in any JavaScript environment. It covers the syntax of the language, basic types like numbers and booleans, and more complex types like strings or arrays, but also “library” objects like Date, RegEx and JSON, so when you have to perform date calculations for example, you can do this by just looking up what methods the Date object provides.

On top of this core language, to actually interact with the application that is hosting the JavaScript environment (web browser, Adobe Acrobat, Node.js server, …) we need to add some application specific “stuff” to the mix. And this is where things differ completely between different JavaScript environments. JavaScript running in the browser knows about web pages, and elements on a web page, HTML connections, and more web specific things, whereas the Acrobat environment does not care about these things, but knows about PDF documents, annotations, form fields and more things that are important in the world of PDF.

Learning the JavaScript Core Language

So, to learn JavaScript for Acrobat, you just take any introductory JavaScript book, class or tutorial and just read and learn the parts about the core language, and ignore the rest. Unfortunately it’s not that simple: Most training resources for JavaScript assume that you are trying to learn to program for the browser environment, so they mix information that belongs into the core language portion with how the script actually interacts with the browser. This can be simple things like how the script is stored: When you write for the browser, chances are that your script actually lives in an HTML document. To interact with the user, your training resource assumes you can get information from the user by using the “prompt()” method, and present information by modifying the current HTML page.

All this makes it a bit more challenging to learn JavaScript for just Adobe Acrobat and the PDF environment.

There is nothing wrong to just take a JavaScript book, start on page 1 and work through the book, following all examples, and actually using the browser to experiment and develop. The problem comes when you then have to unlearn the things you just worked so hard to learn in order to switch to the Acrobat environment.

I am only aware of a couple of resources that provide a fairly clean breakdown of just the core language (that does not mean that there are not more, but I have not seen them. If you know of one, please post in the comments):

Flanagan’s book is the definitive guide, with a large chapter about the core language, but it’s a bit dry and probably not well suited for somebody who is just starting out. For a programmer with a good foundation in any other programming language, this would be a great resource. Simpson’s book is a short introduction into the core language. You can run all the examples from both books in Acrobat if you keep a few simple rules in mind:

Differences (console.log)

Acrobat’s JavaScript console object does not support the log() method. Instead of console.log(“abc”); you will have to use console.println(“abc”);.

This will work in most cases, but the log() method is a bit more powerful than Acrobat’s println(), so you may end up with a few examples for which you will have to modify the arguments to the log call (even though I did not find any when I browsed through the examples in both books):

console.log() concatenates it’s arguments

console.log(“abc”, “def”);

This will print the line “abc def” – it will concatenate the individual strings. This also works with variables:

var car = "Dodge Charger";
console.log(“My first car was a", car);

This will print “My first car was a Dodge Charger”.

To implement this with Acrobat’s println(), you would use the normal JavaScript string concatenation:

var car = "Dodge Charger";
console.println(“My first car was a " + car);

I had to add a space at the end of the first string to get the same output as with log().

console.log() allows substitution strings

var n = 5;
var myName = “John Doe”;
console.log(“My name is %s and %d squared is %d”, byname, 5, 5*5);

This can be rewritten using Acrobat’s util.printf().

In addition to the console.log() function, you also need to change all instances of alert() and prompt() as explained below.

More Books

For any other resource, you have to take the examples presented, and covert them to what Acrobat expects you to use. I’ve looked at two more books that seem to give a reasonably good introduction into the core language, but you will have to pick and choose which areas you need to skip.

In these books (and probably most other JavaScript books), the JavaScript examples are wrapped in HTML, and you have to identify where the script is, extract it and then potentially modify it to make it run within Acrobat. Here is an example of what you might find:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
  <head>
    <title>
      First Example
    </title>
  </head>
  <body>
    <p>
      Some text
    </p>
    <script type="text/javascript">
      alert("This is a message");
    </script>
  </body>
</html>

You can open this as example_1.html and see what it does in your browser.

The only thing that is interesting for us is the text inside the <script> tag – that is everything between <script> and </script>:

    alert("This is a message");

How do we run this code in Acrobat?

Now that we have the script we need to run, how do we run it in Acrobat’s JavaScript console? Thom Parker already did an excellent job explaining this Acrobat feature, so there is no need to do this again.
Here is his tutorial about how to run code in Acrobat’s JavaScript console: https://acrobatusers.com/tutorials/javascript_console

More Differences (alert and prompt)

When we try to run the above line of code, Acrobat will report an error on the JavaScript console:

alert("This is a message");
ReferenceError: alert is not defined
1:Console:Exec
undefined

In this console log, we have four lines: The first line is the one that was executed, the second line gives us an error message, the third line tells us where the error occurred, and the last line shows the return value of what we’ve executed. The message “undefined” sounds bad, that that’s actually what we would expect when running a command that does not return a value – or in this case, when JavaScript command we were trying to run failed.

The JavaScript interpreter is telling us that “alert is not defined”. This is one of these differences between the application specific extensions that sneaks into the description of the core language: Every web browser will display an alert message box when this line of JavaScript gets executed, but Acrobat does not know about the alert() function. Acrobat does however provide very similar functionality via the app.alert() method. See the description in the SDK documentation for more information. We can use the simplest form of app.alert() to replace the alert() call in our example:

  app.alert("This is a message");

After executing this line, a window pops up:

Dialog window that shows 'This is a message' and an 'OK' button

And, I get this in the JavaScript console:

app.alert("This is a message");
1

The first line again is the code I am executing, the second line shows the return value of what got executed. From the API documentation (see link above), we learn that a return code of “1” means that the “OK” button was pressed (which is actually the only button that was on our dialog, but the app.alert() method allows to add more than just one button).

This takes care of informing the user about what our program did. Often there is also a requirement to ask the user for input. In a web browser, the JavaScript program would use the prompt() function, which again does not exist in Acrobat (this is example2.html):

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
  <head>
    <title>
      First Example
    </title>
  </head>
  <body>
    <p>
      Some text
    </p>
    <script type="text/javascript">
      var val = prompt("Enter a value");
      alert("You entered " + val);
    </script>
  </body>
</html>

And just as before, the code we are interested in is within the <script> tag:

  var val = prompt("Enter a value");
  alert("You entered " + val);

We already know what to do with the second line, to replace the prompt() function call with something that Acrobat understands, we will use the app.response() method. For more information about this method, see the Acrobat JavaScript API Reference.

  var val = app.response("Enter a value");
  app.alert("You entered " + val);

This results in these two windows being displayed:

Dialog window asking the user to enter a value. The screenshot shows that the value '23' was entered.

Dialog window that shows the message 'You entered 23'

Any time a script references window or document, we are dealing with a script that cannot be easily be converted to Acrobat’s JavaScript.

A Book Just About JavaScript for Adobe Acrobat

If you are looking for a book that only talks about JavaScript for Acrobat, and also introduces you to how these scripts are used in Acrobat, take a look at John Deubert’s Beginning JavaScript for Adobe Acrobat

Further Steps

Once you have a good understanding of the core language, you need to become familiar with how JavaScript is used in Acrobat. A good introduction is the document “Developing Acrobat Applications Using JavaScript” in the Acrobat SDK, followed by the dry but necessary “JavaScript for Acrobat API Reference“.

If you need any help in learning JavaScript, or in how it is used with and in Adobe Acrobat, keep in mind that I do run a consulting business and part of what I do is to provide training.

Full disclosure: Some of the links to books on this page use my Amazon affiliate link, so when you order through one of these links, I will get a few cents.

Posted in Acrobat, JavaScript | Tagged , , , | Leave a comment

Mark Selected Options with Circles in PDF Forms

I assume you’ve filled out a paper form with a pen, and circled one or more of the options presented on the form. Can this be done in a PDF form as well?

2016 11 11 11 59 18

To create such a form, we cannot just use the standard PDF form field types, we need to be a bit more inventive. A while ago, I answered a question to do just that with two options (“Yes” and “No”) on the now read-only AcrobatUsers.com: https://answers.acrobatusers.com/Circle-PDF-clicking-it-q290981.aspx

For a scenario where more than two buttons need to be part of such a group, or for a more flexible approach, I modified the script presented in the AcrobatUsers.com post.

Here are the steps to a complete solution:

Create a PDF file containing just the “circle” (or the oval) you want to use to circle the options in your form. You can do this in e.g. Adobe Illustrator or Adobe Photoshop. Make sure that the inside of the circle/oval is transparent, otherwise you will not see the selected option “through” the circle.

Create your form with all the options you want to be circled as part of the form “background”. Then create one button field that uses the circle/oval image from above as it’s button icon. This button will not be used to interact with the user, it’s purpose is to store the button icon (the circle/oval), and it will eventually be read-only and hidden, so place it anywhere on the form where it does not interfere with other buttons you want to place. Now bring up the properties dialog for this new button. The selections on the properties dialog that need to be changed are outlined here:

On the “General” tab select to make this button read-only and hidden. Again, we are only using this first button to store the icon image, so there is no need to show this button to the user, or let the user interact with it. I am calling this button “icon”, if you select to change the button name, keep in mind that the same name needs to be used in the button action script below.

On the \

On the “Appearance” tab set both the border color and the background color to “transparent”. This setting also needs to be applied to the other buttons we will add to this form.

On the \

On the “Options” tab select to use an “icon only” layout, and set the “Behavior” to “None”, then select the PDF document that contains your circle/oval icon from above.

On the \

Now add the other buttons to your document that we will use to select options on the form. For now, I am only considereing one group of buttons called “Button1” – the individual buttons in the group will have names like “Button1.Opt1”, “Button1.Opt2” and so on. You can use any descriptive name for the last part of the button name, as long as it does not contain a period.

Setup these buttons with transparent border and background color as described above. Now use the following script as the “Mouse Up” action script on the buttons’ “Action” tab:

var baseName;
var currentState;

// get our name
var theName = event.target.name;

var re = /(.*)\.(.*)/;

var ar = re.exec(theName);

if (ar.length > 0) {
    baseName = ar[1]; 
    currentState = ar[2];

    // make this button visible
    event.target.buttonSetIcon(this.getField("icon").buttonGetIcon());
    event.target.buttonPosition = position.iconOnly;
    
    // hide the other button
    var f_parent = this.getField(baseName);
    var f = f_parent.getArray();
    for (var i in f) {
        if (f[i].name != theName) {
            f[i].buttonPosition = position.textOnly;
       }
    }
}

this.calculateNow();    // this line is only required if the status of the 
                        // selected button needs to be processed

This script will set the button that you clicked on to use the surrounding circle/oval as it’s button image, and it will remove it from all other buttons in the same group. You may notice that we never actually make assumptions about what these options (or the button names) are – it’s all handled automatically.

This should give you the correct behavior for all the buttons on the group – whatever option you select will be circled. However, if the selection needs to be further processed in your form, we have one more hurdle ahead of us: With a “normal” form that uses radio buttons or checkboxes to indicate a selection, it’s very easy to get the selected value. With our “circle the selected item” form, that is not as simple. Let’s say you want add a text field to the form that should display the value you’ve circled. The following code – when used as custom calculation script for that field – will get the current selection, and will then display it in the text field:

var selection = "";

// get the "Button1" group of fields:

var f_button1 = this.getField("Button1");

var f = f_button1.getArray();

for (var i in f) {
    if (f[i].buttonPosition == position.iconOnly) {
        // get the last part of the field name (e.g. Button1.Opt1 -> Opt1)
        var idx = f[i].name.lastIndexOf(".");
        if (idx > 0) {
            selection = f[i].name.substring(idx+1);
        }
    }
}

event.value = selection;

For this to work, the form needs to be recalculated whenever a button is pushed. This does not happen automatically, that’s why we are calling the ‘calculateNow()’ method at the end of the button action scripts.

Here is a functioning PDF file that has all the scripts in it: circle_button.pdf And here is the Adobe Illustrator file with the oval: circle_button_icon.pdf

Posted in Uncategorized | Tagged , , , , | Leave a comment

Adobe Acrobat’s File Open or Save Dialogs are Slow or Hanging – Here is the Fix!

Is your File Open or File Save/Save As dialog slow to open, or does it look like it’s hanging and never actually coming up? If you are running Adobe Acrobat or Adobe Reader version DC, the fix for this is easy:

In Adobe Acrobat DC, the operating system’s file dialogs are replaced by Acrobat’s own dialog that allows you to pick a file from the recently used list, from Adobe’s online services “Document Cloud” and “Creative Cloud”, other online storage solutions (e.g. Dropbox, Box, OneDrive and SharePoint sites), and finally files on your local system.

OpenDialog

It takes time to connect to all these online services to see if they are available, and if one is slow to reply, it may actually look like the dialog is hanging completely.

To avoid these performance problems, these online services can be turned off. To do that, open Acrobat’s Preferences, go to the “General” category and uncheck the two “Show online storage…” options:

Preferences

Once this change is made, Acrobat will use the operating system’s file selection dialog directly. This also means that the online storage services are no longer available via an almost one-click access method. It would be nice to have a way to select which dialog comes up by default, and be able to bring the other one up when necessary without having to go through the Preference settings. If this is something you want to see, join me in filing an Enhancement Request for Adobe Acrobat.

Posted in Acrobat, PDF | Tagged , , , , , , | Leave a comment

Getting a Serial Number Into a Form Using SOAP

One request that shows up again and again on the Adobe Forums (or in the past on AcrobatUsers.com) is about how to generate a unique serial number or form number in a PDF file. The scenario is usually something like this: A user creates documents (e.g. quotes or orders) that need to be numbered in a sequential manner so that no forms will use the same number. There are a number of solution available that work well as long as all these forms are always processed by the same user on the same computer. This is accomplished by storing the number in JavaScript in e.g. another form, or in global JavaScript data.

How can the same functionality be implemented if the requirement is to use this on multiple computers, or by multiple users? Allowing access from multiple environments complicates things quite a bit. To allow concurrent access to the “serial number generator”, we need to move the solution to a server. There are different mechanisms how Acrobat can access a server, for the following discussion, I want to use a web service via the SOAP interface, which is documented in the Acrobat JavaScript API: SOAP and Web Services

Explaining what SOAP is and what it’s used for is outside of the scope of this post. If you want to learn more about SOAP, take a look here: SOAP on Wikipedia

Implementing such a solution requires two parts:

  • A JavaScript program running in the form
  • The actual web service running on a server.

When I say “server’, I use that term in a very broad sense, and that could mean that you run the software on an actual server (running e.g. Windows Server, Mac OS Server, Linux, ..) or on a normal workstation that is running server software. You can install for example the free XAMPP software on a Windows 10 computer and still get the same behavior as you would with a real server system.

Before we get too far into this, when you review the requirements for the SOAP calls in Acrobat’s JavaScript implementation, you will notice the “F” in the quick bar:

2016 06 12 12 42 02

This means that this feature is only available in the free Adobe Reader if “Forms” rights are applied to the document. Even though Adobe Acrobat can apply some extended Reader rights to a document, for the “Forms” rights, the LiveCycle Reader Extensions software is necessary. For the purpose of this tutorial, the presented solution will only work if you are using Adobe Acrobat (version 6 and later) and not the free Adobe Reader.

The solution we want to create is a form that has at least two fields: One that will eventually contain the form number – or serial number – and one that contains the name of the person filling out the form, or the name of the person the form was created for. In addition to these two, the form can contain any number of fields. We also need a mechanism to request a new serial number – in the sample below, that will be done using a button. When a new serial number is generated, I also want to store the user name and the current time and date so that I can go back later and find out what documents were processed and either by what user, or for which customer (depending on how I use that user name field).

To make things easier on the implementation in the actual PDF form, let’s start with creating the web service that provides these unique numbers and stores information about the request to generate such a number.

This web service should provide a function named “getSerialNumber()”, which takes one argument (the user name), and returns a string containing a new number. Web services can be written in any number of languages. Oftentimes web services are written in Java and are then executed in an application server like Apache Tomcat. For this sample implementation, I selected a web service written in PHP. This has the advantage that it will run on Windows, Linux, Mac OS, and any other system that provides support for PHP. The XAMPP system mentioned above does come with a PHP interpreter and Apache’s web server configured to use PHP. Setting up a web service in PHP based on just the documentation is not straight forward, and I found a lot of help here: A Complete PHP SOAP Server Example If you want to understand more about how all these pieces are working together, you may want to spend some time on that site.

The big question now is how can one create a unique and sequential number? I am going to use a database for that: When you define a field as the primary key in a MySQL database, you end up with such a unique and sequential number. For every new record, that index gets automatically incremented, starting with the value 1 for the first record.

The following PHP script implements such a system that writes the user name and the current date to a database, and then returns the index for that new record:

<?php
    function getNextSerial($userName) {
        // open a database connection and insert a new record, get the last used index and return that
        $mysqli = new mysqli("localhost", "theUser", "thePassword", "serialnumbers");

        // check connection
        if (mysqli_connect_errno()) {
            printf("Connect failed: %s\n", mysqli_connect_error());
            exit();
        }

        $query = "INSERT INTO serialnumbers (username, date) VALUES (\"" . $mysqli->real_escape_string($userName) . "\", NOW())";
        $mysqli->query($query);

        $idx = $mysqli->insert_id;	// get the unique index of the just inserted record

        // close connection
        $mysqli->close();
        
        return $idx;
    }
    
    // Disable the wsdl cache
    ini_set("soap.wsdl_cache_enabled", "0");

    // Define the response class for getSerial()
    class getSerialResponse{
        public $return;
    }

    // Define the class and methods for the object that gets passed to setClass 
    class getSerialClass{
        public function getSerialNumber($parameters){
            // Instantiate the response class
            $response = new getSerialResponse();
            // Return the next available serial number
            $response->return = getNextSerial($parameters->userName);
            return $response;
        }
    }   

    // Create a new server
    $server = new SoapServer("GetSerialNumber.wsdl");

    // Set the class for the server
    $server->setClass("getSerialClass");

    // Handle the soap operations
    $server->handle();
?>

This assumes that a WSDL file named “GetSerialNumber.wsdl” is in the same directory as this PHP file, and this WSDL file in turn is referencing a schema file that also needs to be in the same directory.

You can learn about how to manually create a WSDL file here: How to Generate WSDL for PHP SOAP Server – With a recent version of NetBeans, there is one more step involved that is not documented in the tutorial: You need to open the generated WSDL and replace “REPLACE_WITH_ACTUAL_URL” with the actual URL of your web service PHP file.

You can download all files referenced in this post here: GetSerial.zip

So, what does this do? It defines one web service routine called “getSerialNumber()” which takes one string parameter (the user name) and returns a string
containing the new serial number. It does that by calling the “getNextSerial()” function, which opens a connection to a MySQL database and inserts a new record using that user name and the current time and date. It then returns the index of the just inserted record.

Here are the MySQL commands to create the database, the table and a user that can modify the table:

CREATE DATABASE serialnumbers;
CREATE TABLE serialnumbers (idx INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30), date TIMESTAMP);

CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password';
GRANT INSERT,SELECT ON serialnumbers to 'theUser'@'localhost';

Now to the JavaScript code that runs in the PDF form: In my sample document, I have two fields, one name field, and one serial number field plus a button that when pressed takes the information from the user name field, generates a new serial number and populates the respective field and then hides the button so that you cannot keep on clicking on it and therefore “waste” serial numbers.

// Obtain the WSDL proxy object:
var myProxy = Net.SOAP.connect("http://localhost/GetSerial/GetSerialNumber.wsdl?wsdl");

// get the user name from a field
var userName = this.getField("UserName").value;
if (userName != "") {
    var result = myProxy.getSerialNumber(userName);
    this.getField("Result").value = util.printf("%04d", Number(result));
    // hide this button
    event.target.display = display.hidden;
}
else {
    app.alert("Please fill in a user name");
}  

2016 06 12 14 46 36

In the first step, we load the WSDL file that was installed with our PHP web service, then we get the contents of the “UserName” field and if that is a non empty string, we request a new serial number via the SOAP proxy object. We then take that result, interpret it as a number and format it so that it has leading zeros. The last thing we do is to hide the button that was used to generate the serial number. There could be more error checking and exception handling in this code, but I did not want to clutter up the actual functionality with that. For example, you need to check that the user name provided is not longer than 30 characters (that’s what we used to define the length of the VARCHAR field in the database table).

2016 06 12 14 46 58

When you try to use these files, you have to make sure that you adjust all URLs that are used in both JavaScript and PHP code so that they match your installation. I had things installed in http://localhost/GetSerial – you would have to search for that string and replace it with the correct path to the directory in your installation.

My tutorials are usually pretty simple to follow. This is a bit more complex and requires you to setup a web server, the PHP system running in that server, a database server with a database and then at the very end a few lines of JavaScript code in a PDF form. I have to assume that whoever wants to tackle this does know how to work with all these different technologies. If this is a bit too much for you, please ask me for a quote to implement such a solution for you.

Posted in Acrobat, JavaScript, PDF, Programming, Tutorial | Tagged , , , , , , | Leave a comment

Selective Flattening of Form Fields Using ABCpdf

Let’s assume you have a form with a lot of form fields, and somewhere during your forms workflow, you want to flatten some fields to “burn in” the information and convert it from an interactive field to static PDF content. There are different ways to accomplish this. One option is to do this all in Acrobat’s JavaScript. Unfortunately, when you lookup the documentation for the Doc.flattenPages() method, it does not talk about selecting only a subset of fields (unless these fields are all on one specific page, and that page does not contain any fields that should not be flattened). But, there is an optional parameter that allows us to specify what should happen to fields that are set to only be displayed in the viewer, but not be printed: The “nNonPrint” parameter, when set to the numeric value “1”, will leave
these fields alone, and will not flatten them (a value of “0” will flatten them and a value of “2” will remove them from the document).

This allows us to modify the document before we call the Doc.flattenPages() method: If we turn all fields that should not be flattened into non-printing fields, then flatten the document, and then in a last step reverse that first change, we can do flatten only a subset of fields. The problem gets a bit more complex when we already have non-printing fields in the document that we want to remain non-printing, or if we have non-printing fields that we want to flatten. That will require quite a bit of logic to save the original state of all fields, turn those fields we want to flatten into printing form fields, turn the fields that we don’t want to flatten into non-printing fields, flatten the document, and then restore the original settings again. As I said, a bit complex.

As long as we don’t need to do this within Acrobat, there is another solution: The ABCpdf library that I’ve mentioned before provides functionality to flatten fields on a per-field basis. [ Full disclosure: The fine folks at webSupergoo provided me with a free license to ABCpdf 8 based on that old blog post. That’s what I am still using for my experiments. They are up to version 10 by now, and based on their feature comparison chart, there are a few new features in this version that sound interesting. ]

Now, when you search for “flattening” in the ABCpdf manual, you won’t find anything that will help you in this case: The only flattening I found in my version is for flattening layers, and versions 9 and 10 support transparency flattening. Nothing about form fields. It took me a bit of head scratching and exploring the Form and Field APIs to figure out that form or field flattening is actually done via a process called “stamping”. Once that hurdle was cleared, it was pretty simple to come with a routine to flatten just one field at a time. Here is the VB code I’ve used (the same will also work in C# or via any of the other interfaces that ABCpdf supports):

' This assumes that we already have an open document 'theDoc'
Dim theField As Field

' set the field 'Text1' to some value
theField = theDoc.Form.Fields("Text1")
theField.Value = "Some value"

' flatten one form field
theField.Stamp()

' flatten all form fields in the document
theDoc.Form.Stamp()

Just to demonstrate how to flatten a whole document, I’ve added that as the last line in this snippet.

This makes it very simple to e.g. define an array of field names to be flattened, and then just loop over that array, get the field, and flatten one at a time.

This feature in ABCpdf is very useful, and easy to use – even though it has a name that is a bit confusing to somebody who works with a different kind of PDF Stamps on a regular basis 🙂

Posted in PDF, Programming | Tagged , , , | Leave a comment

Batch-Import List Data into PDF Form

We’ve talked about batch importing data from an Excel document into a PDF form before: http://khkonsulting.com/2015/10/batch-import-excel-data-into-pdf-forms/

Back then, the idea was that we would have a spreadsheet with rows of data, and every row would be imported into a new copy of the document (e.g. a mail merge type application).

What if we want to import rows of data into one document? Let’s assume, we have a table in our document with 20 rows, and we have a spreadsheet that has the same 20 rows, and we want to import that data. If we would use the method from above, the whole form data would have to be rewritten into a spreadsheet with just one row of data. Let’s do a simple example with a small table:

Firstname Lastname
John Doe
Jane Doe

The data in the PDF file would be organized like this:

Firstname_1 Lastname_1
Firstname_2 Lastname_2

Our datafile would then look like this:

Firstname_1	Lastname_1	Firstname_2	Lastname_2
John	Doe	Jane	Doe

The blank spaces between the entries in the list above are TAB characters – remember, we need a tab separated text file for this to work.

This is simple to do for a small files, but if you are dealing with 100 records of 10 fields each, we are talking about a pretty extensive row of data. It can certainly be done, especially if a VBA macro is used, but it’s not what I would do.

The good news is that this can still be done using JavaScript. The trick is to work with two documents: We of course start out with the document that we want to populate with data, but in addition to that, we are also using a temporary document that has one set of fields for each record we are going to read. This way, we can read one record at a time, and then copy that record into our final form. This temporary form will be created on the fly using JavaScript. And, because we will never actually look at that form, we don’t have to worry about placing the form fields in any meaningful way. In my example further down, I am placing all fields right on top of each other.

Let’s assume we want to create a sign-in sheet for an event, and we want to print the full name of each participant and the participant’s email address on the form:

Blank form

You can download the documents from these links:

Blank form
Form with fields

The following script can for example be used as an Action, or a custom command in Acrobat DC:

/* Import list data from tab delimited text file */

var dataFile = "/Users/username/data.txt"; // !!! CHANGE THIS !!!

// Create a temporary document and add the three text fields
// that correspond with the three columns in our data file.
var tmpDoc = app.newDoc();
tmpDoc.addField("Firstname", "text", 0, [0, 0, 100, 100]);
tmpDoc.addField("Lastname", "text", 0, [0, 0, 100, 100]);
tmpDoc.addField("Email", "text", 0, [0, 0, 100, 100]);

// Iterate over the data file and import the corresponding record
// from the data file and then copy that data to the corresponding
// data row.
var err = 0;
var idx = 0;
while (err == 0) {
	err = tmpDoc.importTextData(dataFile, idx); // imports the next record

	if (err == -1)
		app.alert("Error: Cannot Open File");
	else if (err == -2)
		app.alert("Error: Cannot Load Data");
	else if (err == 1)
		app.alert("Warning: Missing Data");
	else if (err == 2)
		app.alert("Warning: User Cancelled Row Select");
	else if (err == 3)
		app.alert("Warning: User Cancelled File Select");
	else if (err == 0) {
		// collect the data and add it to the "real" form
		var name = tmpDoc.getField("Lastname").value + ",\n" +
			tmpDoc.getField("Firstname").value;
		var email = tmpDoc.getField("Email").value;

		this.getField("Name" + (idx + 1)).value = name; // we need to adjust the index by one
		this.getField("Email" + (idx + 1)).value = email;
		if (idx == 19) {
			// we can only process 20 records on each sheet
			err = -99;
		}
	}
	idx++;
}

// cleanup
tmpDoc.closeDoc(true);

Here is the sample file I’ve used as Excel Spreadsheet and as tab delimited text file (this is random data thanks to the “Fake Name Generator“.

There are a few potential problems when you are using this approach: First of all, the same limitations that apply to manually importing data apply here as well: You need to make sure that each column in your data file is represented by a field in your temporary file, and that the names match. There cannot be any extra fields either. And, in this particular case, you will have to make sure that you only import up to the same number of records that your document can actually handle. My document uses 20 records, so I am checking for that in my script. If you need continuation pages, you can certainly do that, it would make the script a bit more complex.

If you want to adapt this approach for your own solution, make sure that you add all required fields to your temporary document. In the example above, I am only using text fields, but the same technique can be used for other field types as well.

Let me know if this works for you.

Posted in Acrobat, JavaScript, Tutorial | Tagged , , , , , , | 13 Comments

Using Custom Dynamic PDF Stamps in Adobe Acrobat or Adobe Reader

There is sometimes a misconception about how a custom dynamic stamp works in the PDF environment. Here is a short video that demonstrates how such a stamp gets applied:

As you can see, the custom dialog that collects the data that will be placed as part of the stamp pops up after the stamp is placed. Once all information is entered, and the “OK” button (how that button is labeled is up to the stamp’s implementation) is pressed, that data gets merged into the stamp and the stamp gets displayed. From that point on, that data can no longer be changed. It’s like placing a rubber stamp on a piece of paper. Once it’s there, it can no longer be modified. There are however a few things we can do with a PDF stamp: We can move the stamp, resize and rotate it – as shown in the video.

In the second part of the video, I am demonstrating how to use the “Stamp” tool button on the toolbar. In order to have this button available, you will need to add it to your toolbar. For more information about how to customize the Acrobat user interface, see for example here: http://blogs.adobe.com/acrolaw/2013/03/customizing-the-acrobat-xi-interface/

This article is not supposed to be a “tell all” story about dynamic stamps, I just wanted to demonstrate the basic workflow involved in placing a custom dynamic stamp. If you have questions about these types of stamps, please post in the comments.

Posted in Acrobat, PDF | Tagged , , , , , | 7 Comments