Techniques of Introducing Document-Level Javascript Into A File From A L Tex Source

Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

Techniques of Introducing Document-level JavaScript into a PDF file from a

LATEX Source

D. P. Story
Department of Mathematics and Computer Science, University of Akron, Akron, OH 44325
[email protected]
http://www.math.uakron.edu/~dpstory/

Abstract
The method of introducing Acrobat document-level JavaScript (DLJS) into a
PDF depends on the application used: pdftex or dvipdfm. Until recently, users
of Acrobat’s distiller did not have the ability to automatically introduce such
JavaScript into the document from a LATEX source. For users of Acrobat 5.0, this
situation is, at last, rectified. The focus of this paper is to enumerate, illustrate
and discuss these various methods.
A new package, insDLJS, is also introduced. This package enables both
package and document authors to insert document-level JavaScript.

Introduction of developing an educational document, many revi-


sions to the document are typically made and the
Adobe’s Portable Document Format (PDF) has be-
JavaScript must be reinserted each time the PDF is
come widely used by some as a document exchange
built. Ideally, the JavaScripts should appear within
format; LATEX users routinely convert their research
the LATEX source file, which makes it easy to access
papers and technical documents to PDF and post
and modify them, and then automatically inserted
them on the Internet, or e-mail them to a inter-
when the PDF is built.
ested colleague. When the LATEX package hyperref,
The applications pdftex and dvipdfm both sup-
written by Sebastian Rathz and Heiko Oberdiek,
port the insertion of DLJS from a LATEX source.
is used, cross-references created by standard LATEX
Historically, it was not possible to insert DLJS from a
commands are automatically converted into hyper-
LATEX source through the distiller method, and sadly,
links within the PDF document.
this is still the case. However, the Acrobat applica-
However, LATEX and PDF are capable of more
tion, version 5.0, does include some extensions that
than just producing an electronic copy of a paper
can be exploited to obtain an automatic insertion
document. By utilizing LATEX’s package customiza-
of DLJS. For version 5.0, it is the Acrobat applica-
tion feature (LATEX packages) and Acrobat’s form el-
tion that inserts the DLJS automatically following
ements and powerful JavaScript language, an author
distillation, not the distiller itself.
can build a colorful, richly interactive PDF. Such a
The discussion that follows—for the case of
document can be used as a teaching tool that would
production of PDF by the distiller method—assumes
quicken the interest of the student.
that the author has the full Acrobat 5.0 product. It
The focus of this paper is on JavaScript. Java-
is important to note that it is only necessary for
Script can be attached to a PDF document in many
the author to have full Acrobat 5.0; the user, the
ways, for example, to a form button. JavaScript
one who reads the document, needs only have the
attached to buttons can be used to initiate actions.
Acrobat Reader (version 4.0 or later).
Any scripts that are repeatedly used, are general
(not form specific), or are rather lengthy, can be Inserting DLJS
placed at what Acrobat calls the document-level ;
they are called document-level JavaScripts (DLJS). We begin by discussing the basic techniques for each
of the common applications used for producing a
A button action, then, can simply call these DLJS
PDF document. Suppose we wish to introduce the
to perform various calculations or tasks. following JavaScript function at the document level.
For authors or package writers who really want
function HelloWorld()
to tap into the power of JavaScript, document-level {
scripts need to be written that perform the target app.alert("Hello World!", 3);
task and embedded into the PDF. In the context }

TUGboat, Volume 22 (2001), No. 3 — Proceedings of the 2001 Annual Meeting 161
D. P. Story

A variation on a rather well-known, yet useless \edef\objNames{\the\pdflastobj\space 0 R}


function. \pdfnames {/JavaScript \objNames}
In the sections that follow, it is assumed the }
source document uses the hyperref package, with The control sequences \pdfobj and \pdfnames are
the appropriate driver setting: dvips, dvipsone, pdftex primitives.
pdftex and dvipdfm. Some of the commands in the
code below have their definitions in the hyperref dvipdfm Similarly, for dvipdfm, we need only in-
clude the following code to get the DLJS:
package.
Within a package, or preamble, make the fol- \AtBeginDocument{%
lowing definitions. This code is common to all cases, \immediate\@pdfm@mark{obj @objDLJS
pdftex, dvipdfm and distiller. << /S /JavaScript /JS (\DLJS) >> }
\immediate\@pdfm@mark{obj @objnames
\def\jsR{\string\r} % carriage return in JS
<< /Names [(Doc Level JS) @objDLJS] >> }
\def\jsT{\string\t} % tab in JS
\@pdfm@mark{put @names
\begingroup\obeyspaces\obeylines%
<< /JavaScript @objnames >> }
\global\let^^M=\jsR%
}
\gdef\DLJS{%
function HelloWorld() The command \@pdfm@mark is defined in terms of a
{ dvipdfm \special,
app.alert("Hello World!", 3); \def\@pdfm@mark#1{\special{pdf: #1}}
}
as defined in hyperref.
}%
\endgroup Multiple Functions More than one function can
This defines a macro that expands to the code of the be grouped together using the obvious approach:
desired JavaScript function. Note that \obeyspaces \begingroup\obeyspaces\obeylines%
and \obeylines are used; \global\let^^M=\jsR \global\let^^M=\jsR%
will insert a \r at the end of each line of the \gdef\DLJS{%
JavaScript code, this nicely formats the script within function HelloWorld()
{
the PDF file itself.
app.alert("Hello World!", 3);
pdftex and dvipdfm The approach to DLJS inser- }
tion is similar in the case of these two applications, function GetNumberofPages()
both of which fully support, through primitives or {
app.alert("The number of pages is "
specials, DLJS insertion.
+ this.numPages);
In each case, insertion is a two step process:
}
(1) create an PDF object dictionary containing the
}%
necessary key-value pairs
\endgroup
<< /S /JavaScript /JS (\DLJS) >>
Now, we proceed as described above.
and (2) enter the script name and reference offset An alternate approach is to have separate def-
into the Names array of the JavaScript dictionary. initions for each of you functions, say, \DLJSi and
The implementation of these two steps is differ- \DLJSii, and insert each of them separately into the
ent for these two applications. JavaScript dictionary. For example, in the case of
We wrap the code in the next subsections in a pdftex, we could include the following code:
\AtBeginDocument command. For private macros, \AtBeginDocument
this is not necessary, but for public macro packages {%
it is useful to delay the insertion of the code until \immediate\pdfobj
after the preamble. { << /S /JavaScript /JS (\DLJSi) >> }
\xdef\objDLJSi{\the\pdflastobj\space 0 R}
pdftex For pdftex, the following is all the additional \immediate\pdfobj
code needed to get DLJS insertion: { << /S /JavaScript /JS (\DLJSii) >> }
\AtBeginDocument \xdef\objDLJSii{\the\pdflastobj\space 0 R}
{% \immediate\pdfobj {%
\immediate\pdfobj << /Names
{ << /S /JavaScript /JS (\DLJS) >> } [
\xdef\objDLJS{\the\pdflastobj\space 0 R} (Doc Level JS) \objDLJSi\space
\immediate\pdfobj {% (More DLJS) \objDLJSii
<< /Names [(Doc Level JS) \objDLJS] >> } ]

162 TUGboat, Volume 22 (2001), No. 3 — Proceedings of the 2001 Annual Meeting
Techniques of Introducing Document-level JavaScript into a PDF file from a LATEX Source

>> } \AtBeginDocument{\literalps@out{%
\edef\objNames{\the\pdflastobj\space 0 R} [ {Page1} << /AA << /O << /S /JavaScript /JS
\pdfnames {/JavaScript \objNames} (%
} if(typeof HelloWorld == "undefined")\jsR\jsT
Similarly for dvipdfm. this.importAnFDF("dljs.fdf");
Acrobat expects the script names of the DLJS )
>> >> >>
(these are ‘Doc Level JS’ and ‘More DLJS’, in the
/PUT pdfmark}}
example above) to be sorted ; otherwise, the Acro-
bat application does not give editing access to the This code creates an open page action for page 1
scripts that are ‘out-of-sorts’.1 The scripts would of the PDF. When the document is first opened
be accessible from within Acrobat or Reader, but in the Acrobat application, usually immediately fol-
would not necessarily be available for editing within lowing distillation, if the function HelloWorld is not
Acrobat. already defined, the JavaScript method importAn-
FDF will import the specified FDF into the docu-
Acrobat 5.0 For authors that use Acrobat 5.0 (those ment; otherwise, the open action does nothing. The
that use dvips or dvipsone to produce a PostScript JavaScript function HelloWorld will be placed at
file and then distill) the problem is a little more the document-level because of the structure of the
complicated. Version 5.0 comes with an extended FDF file.
FDF (Forms Data Format) specification. This new
specification allows DLJS to be placed within the A Simple Implementation The FDF can be cre-
FDF file. The FDF file is then imported into the
ated and edited as a separate file; however, our
document and the DLJS is inserted. goal was to have all code contained in the LATEX
source file itself. A simple solution would be to use
The Concepts Take our basic HelloWorld Java- a verbatim write to write the necessary code to a
Script function, and place it into the FDF file, which FDF file.
I’ll call dljs.fdf, as follows: For example, consider the following code. It
%FDF-1.2 is assumed that the verbatim package has been
1 0 obj loaded.
<< \makeatletter
/FDF \newwrite \dljs@FDF
<<
/JavaScript % open a stream
<< \immediate\openout\dljs@FDF=dljs.fdf
/Doc 2 0 R \let\verbatim@out=\dljs@FDF
>>
>> % verbatimwrite Environment: Writes to current
>> %\verbatim@out. Based on examples and macros
endobj % of the verbatim package. Set \verbatim@out
2 0 obj % before calling this macro
[ (Doc Level JS) 3 0 R ] \newenvironment{verbatimwrite}
endobj {\@bsphack
3 0 obj \let\do\@makeother\dospecials
<<>> \catcode‘\^^M\active
stream \def\verbatim@processline{%
function HelloWorld() \immediate\write\verbatim@out
{ {\the\verbatim@line}}%
app.alert("Hello World!",3); \verbatim@start}%
} {\immediate\closeout\verbatim@out\@esphack}
endstream
endobj Now write the FDF file from the LATEX source.
trailer << /Root 1 0 R >> \begin{verbatimwrite}
%%EOF %FDF-1.2
1 0 obj
Once this file has been created, it can be in-
<<
serted into the PDF file by including the following /FDF
code in your LATEX document: <<
1 Neither pdftex or dvipdfm sorts the Names array by script /JavaScript
names. <<

TUGboat, Volume 22 (2001), No. 3 — Proceedings of the 2001 Annual Meeting 163
D. P. Story

/Doc 2 0 R macro package. Such a person typically uses only


>> one system—pdftex, dvipdfm, or the distiller—to cre-
>> ate the materials. These macros cannot be used
>> conveniently as part of a package, where the user—
endobj the one using the package—may want to modify
2 0 obj
the script and/or define a custom script. A general
[ (Doc Level JS) 3 0 R ]
endobj
insert DLJS package is needed. In this section, we
3 0 obj discuss a new package called insDLJS.
<< The package insDLJS takes the basic techniques
>> already discussed, modifies them so that the inser-
stream tion will be friendly to package authors who use
function HelloWorld() insDLJS to write LATEX packages, and to document
{ authors who want to jazz up their documents with
app.alert("Hello World!", 3); DLJS as well.
} The package has four driver options: dvipsone,
endstream dvips, pdftex and dvipdfm. The insDLJS also de-
endobj fines a new environment called insDLJS; the syntax
trailer is
<<
\begin{insDLJS}[<func>]{<base>}{<name>}
/Root 1 0 R
<JavaScript functions or exposed code>
>>
...
%EOF
...
\end{verbatimwrite}
\end{insDLJS}
Use pdfmark to import the FDF when Acrobat
where,
is first opened.
\AtBeginDocument{\literalps@out{% #1: This optional parameter, <func>, is required for
[ {Page1} << /AA << /O << /S /JavaScript /JS the dvipsone and dvips options; otherwise it
(% is ignored. It’s value must be the name of one
if(typeof HelloWorld == "undefined")\jsR\jsT of the functions defined in the environment.
this.importAnFDF("dljs.fdf"); #2: This parameter, <base>, is an alphabetic word
) with no spaces. It is used to build the names of
>> >> >>
auxiliary files and to build the names of macros
/PUT pdfmark}}
\makeatother
used by the environment.
#3: The third parameter, <name>, is the script name
Once the DLJS has been inserted, save the file
of your JavaScript. This name appears in the
(using “Save As”). The DLJS now is saved with the “JavaScript Functions” dialog of Acrobat, un-
file. When the document is opened in Acrobat (or der the menu
Reader) the JavaScript is available to be executed.
You can delete the open action at this point. Tools > JavaScript > Document JavaScripts..

Multiple Functions Multiple functions can either This environment writes one file (<base>.def),
be written to the same FDF file, or to separate or possibly two files (<base>.def and <base>.fdf).
FDF files. When you import multiple FDF files into In the case of pdftex or dvipdfm options, the
Acrobat using importAnFDF, their script names are <base>.def, which contains the necessary defini-
automatically sorted, see the footnote at the end of tions and code, as discussed in ‘pdftex’, page 162
‘Multiple Functions’, page 162. and the following section on ‘dvipdfm’, is read back
into the output document to set the DLJS code.
Plain TEX Users The above examples illustrate In the distiller case, <base>.def is read in and
the basics of DLJS inclusion. Plain TEX users can second file, <base>.fdf, is written. It is the file
(easily) adapt these techniques to plain TEX. This <base>.fdf that is imported into Acrobat following
is left as an exercise. distillation, as outlined on page 163.
The DLJS defined within the insDLJS environ-
The insDLJS Package
ment in a package and in a preamble of a document
All the code described earlier works very well, and will be included in the PDF document automatically.
is adequate for someone trying to develop materials It is important to choose a base name, <base>, and
for the WWW or for a CD-ROM using a private a script name, <name>, different from any that has

164 TUGboat, Volume 22 (2001), No. 3 — Proceedings of the 2001 Annual Meeting
Techniques of Introducing Document-level JavaScript into a PDF file from a LATEX Source

already been declared earlier; otherwise, code will \newcommand\Hello{"Hello World!"}


be overwritten or simply not appear. \begin{insDLJS}
function HelloWorld()
Example 1. The following example assumes the {
driver is either dvipsone or dvips; in these cases app.alert(@Hello, 3);
the first parameter is required. }
\begin{insDLJS}[HelloWorld]{mypkg}{Pkg DLJS} \end{insDLJS}
function HelloWorld() The script included in the output file (.dvi or .pdf)
{ using the \AtBeginDocument mechanism. The user
app.alert("Hello World!", 3); then has the opportunity to redefine \Hello to, say,
}
\end{insDLJS} \renewcommand\Hello{"Bonjour le Monde\space !"}
This would result in the automatic insertion of the Now the HelloWorld function will create an alert
specified JavaScript at the document-level when the dialog that says, “Bonjour le Monde !”.
PDF is built. Important. Note that ‘@’ is used as the escape
In this case, the importAnFDF method (Java- character within the environment. The insDLJS
Script) is used, as discussed on page 163. The value environment eventually leads to a verbatim envi-
of the optional argument, ‘HelloWorld’, is used to ronment. The at-char, ‘@’, has its catcode changed,
detect whether the script has already been imported. \catcode‘\@=0; this will allow macros to expand
The environment generates the following code: within the verbatim environment. The backslash
\AtBeginDocument{\literalps@out{% ‘\’ is needed in JavaScript since it is used as an
[ {Page1} << /AA << /O << /S /JavaScript /JS escape. JavaScript does not use the ‘@’ symbol at
(% all, so we are free to use it here.
if (typeof HelloWorld == "undefined")\jsR\jsT
this.importAnFDF("mypkg.fdf"); A Complete Example The following listing is a
)
complete example of usage of the insDLJS package.
>> >> >>
/PUT pdfmark}} \documentclass{article}
\usepackage[pdftex]{hyperref}
The optional argument is ignored and need not even
\usepackage[pdftex]{insdljs}
be included when pdftex or dvipdfm is specified as \newcommand\tugHello{Welcome to TUG 2001!}
a package option. \begin{insDLJS}{mydljs}{Private DLJS}
Example 2. A LATEX package writer may want to function HelloWorld()
use the insDLJS environment to insert DLJS into {
the package; also, a document author who uses such app.alert("@tugHello", 3);
a package may want to define additional DLJS. In }
this case, just place the code in the preamble, for \end{insDLJS}
example, \begin{document}
\begin{Form} % needed for \PushButton
\begin{insDLJS}{dljs}{Private DLJS}
\section{Test of the \texttt{insDLJS} Package}
function tugWelcome()
% use built-in form button from hyperref
{
Push \PushButton[name=myButton,
app.alert("Welcome to TUG 2001!", 3);
onclick={HelloWorld();}]{Button}
}
\end{Form}
\end{insDLJS}
\end{document}
The script will be automatically introduced into the
PDF document. A Double-Edged Problem In the process of ex-
The Exerquiz Package2 has been rewritten to tending the exerquiz package to include the ability
use the insDLJS package. This not only makes the to evaluate objective-style questions using DLJS, one
JavaScript code easier to read, modify and maintain, big problem was encountered; consider the following
it also allows users of Exerquiz to add in their own example:
DLJS. Exerquiz uses the base name of exerquiz. \begin{insDLJS}[<func>]{<base>}{<name>}
function TestForLParen(string)
Example 3. A package author who uses the pack-
{
age insDLJS to insert DLJS may wish the package
if (/\(/.test(string))
user to modify the scripts in some authorized way.
app.alert("Found Left Parenthesis!",3);
For example, the package author may include
}
2 CTAN:macros/latex/contrib/supported/webeq \end{insDLJS}

TUGboat, Volume 22 (2001), No. 3 — Proceedings of the 2001 Annual Meeting 165
D. P. Story

The Problem. The above script will search the using the pdftex/dvipdfm option, it would expand
parameter string for a left parenthesis, ‘(’. When to var x = "\\\\". Tricky!
dvipsone or dvips is used (i.e., the distiller is used), Here is a final complete example.
the <base>.fdf file is created, imported into the \documentclass{article}
PDF file and the script works correctly. When pdftex
\usepackage[dvipdfm]{hyperref}
or dvipdfm is used this code does not work! When \usepackage[dvipdfm]{insdljs}
the file is opened in Acrobat an exception is thrown,
and an error message appears: \newcommand\tugHello{Welcome to TUG 2001!}
SyntaxError: unterminated parenthetical (
In the case of pdftex and dvipdfm, the JavaScript \begin{insDLJS}{mydljs}{My DLJS}
code is written directly to the PDF file, bypassing function HelloWorld()
distiller. The problem with the above code turns out {
to beunbalanced parentheses. (Acrobat treats ‘\(’ as app.alert("@tugHello", 3);
a left parenthesis, not an escaped special character.) }
All special characters, therefore, need to be escaped; function TestForLParen(string)
we want {
if (/@e\@e(/.test(string))
\begin{insDLJS}[<func>]{<base>}{<name>} app.alert("Found Left Parenthesis!",3);
function TestForLParen(string) }
{ \end{insDLJS}
if (/\\\(/.test(string))
app.alert("Found Left Parenthesis!",3); \begin{document}
} \begin{Form}
\end{insDLJS}
This code now works when the option pdftex or \section{Test of the \texttt{insDLJS} Package}
dvipdfm is used; however, when this code is used
with the distiller (with the dvipsone or dvips op- \begin{itemize}
tion), Acrobat 5.0 crashes! \item \PushButton[name=myButton,
onclick={HelloWorld();}]{Button:}
The Solution. We have two opposing problems, \item \TextField
solving one creates another. The solution is to intro- [name=myText,width=2in,
duce a command, \e. (The ‘e’ is for escape.) The keystroke={if(event.willCommit)
macro has two definitions: for the dvipsone and TestForLParen(event.value);}
dvips options, we define \gdef\e{}; for pdftex and ]{Text Field:}
dvipdfm options, we make the following definitions: \end{itemize}
\catcode‘\@=0 @catcode‘@\=12 @gdef@e{\}
\end{Form}
Note that in all cases, we do change the catcode of \end{document}
‘@’ to \catcode‘\@=0. A button and a text field are created. Click on the
Thus, to get the function TestForLParen to be
button to get an alert dialog. Enter some text in
properly defined for all options, we must type:
the field; when the data is committed, the function
\begin{insDLJS}[<func>]{<base>}{<name>} TestForLParen is called, and the field is scanned for
function TestForLParen(string)
a left parenthesis.
{
if (/@e\@e(/.test(string)) A Note to pdftex and dvipdfm Users If you
app.alert("Found Left Parenthesis!",3); plan to make extensive use of DLJS, and to write
} complex functions, such as the ones that appear
\end{insDLJS}
in the exerquiz package, the purchase of the full
This problem is not limited to regular expres- Acrobat suite is strongly recommended.
sions. Anywhere there is an escape character ‘\’, The only way to debug Acrobat JavaScript is
there is a potential for problems. For example, if you through the JavaScript edit window of the Acrobat
want to assign the variable x a value of ‘\’, you would viewer. If you only use Acrobat Reader, there is
have to say, within some insDLJS environment:
very little feedback as to what goes wrong with
var x = "@e\@e\"; a particular script; development and debugging of
app.alert(x, 3); scripts will be a very long and tedious process.
In systems using the dvipsone/dvips, this would All the JavaScripts in the exerquiz package were
expand to var x = "\\"; in contrast, on systems first written from within the JavaScript editor and

166 TUGboat, Volume 22 (2001), No. 3 — Proceedings of the 2001 Annual Meeting
Techniques of Introducing Document-level JavaScript into a PDF file from a LATEX Source

tested. Once the script was operating correctly, it ented packages. With them, an author can
would be copied, and pasted into the exerquiz source develop a document with page size suitable for
file for automatic inclusion. presentations or on-line viewing. These pack-
ages come with other bells and whistles as well.
Final Comments
3. The exerquiz6 package (D. P. Story) defines
Acrobat Reader provides a forms capability and a several environments for creating exercises and
powerful JavaScript engine that can be exploited to quizzes, both multiple choice and fill-in. This
create dynamic, interactive PDF documents. The package makes extensive use of the form fea-
LATEX system with its flexible “plug-in” capabil- tures and (document-level) JavaScript.
ity (LATEX packages) is a solid authoring tool for 4. The insDLJS package gives the package or doc-
creating high-quality typeset content. The LATEX ument author an easy and convenient method
packages that give access to PDF are of including document-level JavaScripts.
1. The hyperref3 package written by Sebastian There are many other packages (e.g., for graphics
Rathz, Heiko Oberdiek, et al., automatically insertion, color creation) too numerous to mention.
creates bookmarks and cross-reference hyper-
links, allows document information fill-in and As a result of the above mentioned packages,
provides basic form creation code, to mention as well as the various applications for producing
a few. This package is fundamental to any PDF (pdftex, dvipdfm and the distiller), an author
author who wants to create PDF documents for now has a fairly complete set of authoring tools for
distribution over the Web. developing such a colorful, visually attractive and
2. The webeq4 (D. P. Story) and pdfscreen5 highly interactive documents.
(Radhakrishnan CV) packages are screen ori-
3 CTAN:macros/latex/contrib/supported/hyperref
4 6 CTAN:macros/latex/contrib/supported/webeq
CTAN:macros/latex/contrib/supported/webeq
5 CTAN:macros/latex/contrib/supported/pdfscreen

TUGboat, Volume 22 (2001), No. 3 — Proceedings of the 2001 Annual Meeting 167

You might also like