Image Mapping in PDF – Create a JavaScript Color Picker

Among the many feats of ‘HTML emulation’ of which PDF is capable, one of the
less-appreciated (but more remarkable) such feats is image-mapping. This is
the familiar web-page trick where you click on a given spot in an image,
and the X-Y coordinate info from your click is used to determine what pops
up next on your browser’s screen.

For example, if you’re looking at information about a company’s products and you want to be contacted by a sales rep, there may be a web page where you can click on a map of your state or province, thereby triggering a web page that shows the name or phone number for the sales rep in your area. This kind of trick is surprisingly easy to pull off in PDF. As with HTML, the hard part of image mapping is getting the back-end magic to happen on the server. If your hosting service will let you write CGI scripts (in Perl, say), you’re 90% of the way there.

In this article, I’ll show you how to set up an image map in a PDF
file and couple it to a Perl script running on a server. The actual example
used in this exercise is a color-picker swatch. The idea is that when the
user clicks on a given spot in the swatch, the mouse coordinates (which get
sent to our server script) can be used to derive red, green, and blue
values that correspond to the RGB color the user clicked on. We can then
serve those red, green, and blue values back to the user in the form of FDF
data. The FDF data will populate textfield values in a form of our choice.
In this simple example, we just populate the color-channel fields in the
input form. A simple JavaScript routine then converts those values to an
RGB color; and we use that RGN color as the background color in an
otherwise-empty ‘mini-swatch’ textfield, which gives visual feedback as to
the color the user has chosen.

Color Picker

Notice that the
cursor is over an orange area of the picker swatch. The mouse has been clicked at this
position, causing the mini-swatch in the lower left portion of the screen to be filled with
the selected color, but only after a round trip to the server. A script on the server uses
the mouse-hit info to generate the color in the mini-swatch.

To see how it all works, let’s talk, first, about how image-mapping can
be set up in PDF.

Image Maps in PDF

There is no interface to image mapping in Acrobat, unfortunately. That’s
the Bad News. The Good News is that you can set this feature up quite
painlessly by following a few steps. The first thing to remember is that
image maps are associated with links, not with form fields. And in order
for this to work, your link has to be to an URL on the World Wide Web. So
do this:

  1. First, drag out a link bounding box using Acrobat’s Link Tool.

  2. When the link is sized the way you want it, use the Action Type popup
    menu in the Create Link dialog box to choose World Wide Web Link. This will
    cause the button at the bottom of the dialog to change to ‘Edit URL…’

  3. Click the ‘Edit URL…’ button. Enter the URL address where you
    expect to place your server script (the script that will deal with the
    incoming mouse-hit info). But don’t dismiss the dialog just yet!

  4. After you’ve typed your URL, add the following string to the URL:
    -/IsMap true. That should be 12 characters, beginning with a hypen
    and ending with the lowercase word ‘true.’ Dismiss all dialogs and Save the
    PDF file.

  5. Open your PDF file in a text editor. Search for the string ‘-/IsMap
    true)’ and replace it with ‘) /IsMap true’. When you are done there should
    be no parenthesis after ‘true.’ There should, however, be a closed-paren
    after the web URL of your link, and the original hyphen should be a space.
    What we are doing here is adding a new dictionary entry, /IsMap
    true
    , to your link’s action. If you carry this out exactly as
    described, you will not change the file length of your PDF file and there
    should be no internal restructuring of the file (that is, the ‘xref’ table
    offset will be undisturbed). Save the file.
  6. Reopen the PDF file in Acrobat or Reader. Check to see that when you
    leave the mouse over a ‘hot’ part of the link bounding box, the URL name
    shows up in the yellow hint-box, followed by a question mark and the mouse
    X-Y coordinates. The coordinates will change as you move the mouse. (Pretty
    amazing, isn’t it?)

If you saw the X-Y coordinates of the mouse in step six, it means
everything went okay and you now have a ‘map’ under the link area. Any
click on this link whne the file is being viewed in a browser should cause
the browser to emit a GET operation to the URL in the link, with
mouse-coordinates appended following a question mark. This is what you
want.

The Picker

Our example file uses a link over the top of a color-swatch. The
color-picker swatch is a 300-by-100-pixel bitmap created in Photoshop using
Jim Bumgardner’s excellent freeware plug-in, Expression 3.0b. The way
Expression works is that it allows you to enter C-like expressions
(containing arithmetic operations) for the various color channels, using
certain predefined variables such as ‘x’ for the x-coordinate of the
current pixel and ‘w’ for the overall width of the bitmap (in pixels).
Thus, if you were to enter an expression of ‘x/w’ for, say, the red channel
of an RGB image, you would fill the red channel with a color ramp, with
values ranging from zero at the lefthand edge of the bitmap to 1.0 at the
right edge. In Photoshop, pixel values between zero and 1.0 will simply
result in black, since Photoshop expects color values to be in the range
0..255. Hence, a better way to get a ‘red ramp’ in Photoshop would be to
use ‘255*x/w’ (255 times ‘x’, divided by the image width) in the Red
channel of Expression (and zero in the other channels). Expression also
allows the use of trigonometric functions, so you can vary colors
sinusoidally across a given swatch.

Our 300×100 color-picker image was created with the following formulas
in Expression:


RED = 255-(y/h)*255*(1+sin(6.3*x/w))/2

GREEN = 255-(y/h)*255*(1+cos(6.3*x/w))/2

BLUE = 255-(y/h)*255*(1-sin(6.3*x/w))/2

Acrobat uses a normalized representation of RGB space, in which channel
values go from 0..1, so to get Acrobat color-channel values from the above
expressions, just substitute 1.0 for 255.

In making the PDF file containing the color map, I applied the color map
as a button icon, stretched to 522 by 176, but found that the resulting PDF
file was over 100 Kbytes in size. To get the filesize back down, I saved a
Postscript version of the file and Distilled it with JPEG compression set
to Maximum quality, which brought the file to less than 10K. (In a color
map of this sort, the smooth, gradual color changes in the map cause JPEG
to be very efficient, giving excellent compression even with quality set to
Maximum.) I then added some text fields, which brought the total file size
to just over 14,000 bytes.

To make the swatch a true image map, I added a link, following the six
steps outlined above.

Technically, the file ends up being a PDF form since there are
textfields in it. But it’s a form with no Submit button! Notice that what
activates the form is a mouse hit in the color-swatch area (the image map).
This triggers a GET operation back to the server.

At the Server

A simple Perl script (10 expressions long) at the web URL specified in
our link does all the ‘hard work’ (if it can be called that) of processing
the image hit. Since the script is triggered by a GET operation, we can
retrieve the mouse coords in the QUERY_STRING environmental variable. Our
job is to use the X and Y coordinates to derive red, green, and blue
color-channel values using the above-mentioned formulas. The Perl code for
doing this is very straightforward. (The entire script is reproduced
below.)


#!/usr/bin/perl
use CGI qw/:standard/;

$query = $ENV{‘QUERY_STRING’};
print header(‘application/fdf’);
print GenerateFDF($query);

# ——————————————-
sub GenerateFDF {

my ($x,$y) = split /,/,$_[0];

my $red = 1.0-($y/176)*(1+sin(6.3*$x/522))/2;
my $green = 1.0-($y/176)*(1+cos(6.3*$x/522))/2;
my $blue = 1.0-($y/176)*(1-sin(6.3*$x/522))/2;

$fdfString =<%FDF-1.2
%?„œ’
1 0 obj
<<
/FDF << /Fields [ << /V ($blue)/T (b)>> << /V
($green)/T (g)>> << /V ($red)/T (r)>> << /V
(ColorPicker)/T (title)>>
]
/F (http://www.acroforms.com/archive/picker.pdf)>>
>>
endobj
trailer
<<
/Root 1 0 R
>>
%%EOF
END_FDF

return $fdfString;
}

As you can see, the main routine is only three lines long. All the work
is done in the subroutine, GenerateFDF(). The first line of that subroutine
just splits the incoming mouse coords at the comma and puts the raw numbers
into Perl variables, $x and $y. The next three lines derive the
color-channel values ($red, $green, and $blue) using the appropriate math.
Finally, we stuff a Perl variable ($fdfString) with raw FDF containing our
red, green, and blue values, using Perl’s handy ‘HERE statement’ construct.
The returned string contains the entire FDF output, which gets written out
to the HTTP connection back to the browser (after first writing the
appropriate MIME-typed header info).

Bottom line: We send back a dynamically generated FDF response
containing RGB info for the hit. Note that the /F entry in the FDF data
specifies the URL of the PDF file we want our data to be merged into. In
this case, we’re simply merging the data into the same PDF form that
originated the GET, but in theory we could send FDF to any PDF form of our
choosing. That means, for example, that Form A could (based on a user
button-hit) cause the color-picker form to be called up; and a hit in the
color-picker form could cause control to return to Form A.

In Conclusion

In this short but fairly sophisticated example we’ve seen how to set up
a link to be an ‘image map’ in PDF; how to process the mouse-hit data at
the server; how to serve dynamic FDF back to the client; and how to use the
returned data to do something useful back at the client’s machine (i.e.,
display a mini-swatch showing the selected color to the user). It’s
significant to note that neither the PDF form nor the server script need be
long or complex. In this example, the PDF form was barely 14K in size and
the Perl script was only ten expressions long. And since FDF was used for
the transmission of data back to the client, we avoided having to re-serve
large amounts of HTML or other data; in fact, our FDF stream was only 200
bytes long. With these sorts of filesizes, lightning-fast response times
are possible, even on slow networks.

Another win-win situation for PDF.

You May Also Like

About the Author: Kas Thomas

Leave a Reply