ColdFusion: iText / Add JavaScript To Form Example
I occasionally see questions about adding javascript to existing pdf's. There are (of course) some great examples on the iText site. I thought this CF translation of the AddJavaScriptToForm example might be helpful. If you review the code and comments, it actually simpler than it looks. There are some minor differences to compensate for the older iText jars in CF9 and CF8. As well as some name changes to avoid reserved word conflicts.
Source: http://1t3xt.info/examples/browse/?page=example&id=438
Javascript Code (CF8 + CF9)
<cfsavecontent variable="jsCode">
function setReadOnly(readonly) {
var partner = this.getField('partner');
if(readonly) {
partner.value = '';
}
partner.readonly = readonly;
}
function validate() {
var married = this.getField('married');
var partner = this.getField('partner');
if (married.value == 'yes' && partner.value == '') {
app.alert('please enter the name of your partner');
}
else {
var prop = new Object();
prop.cURL = "http://1t3xt.info/examples/request.php";
prop.cSubmitAs = "HTML";
this.submitForm( {
cURL: "http://1t3xt.info/examples/request.php",
cSubmitAs: "HTML"
}
);
}
}
</cfsavecontent>
1) Create a Form (CF9 + CF8)
Note: CF8 does not support "finally". So for compatibility, just change the try/finally clause to a try/catch.
<cfscript>
outputPath = ExpandPath("form_without_js.pdf");
document = createObject("java", "com.lowagie.text.Document").init();
try {
stream = createObject("java", "java.io.FileOutputStream").init( outputPath );
writer = createObject("java", "com.lowagie.text.pdf.PdfWriter").getInstance(document, stream);
document.open();
Element = createObject("java", "com.lowagie.text.Element");
BaseFont = createObject("java", "com.lowagie.text.pdf.BaseFont");
//Note: CF8 + CF9 iText versions do not have a createFont() method with zero params
bf = BaseFont.createFont( BaseFont.HELVETICA, BaseFont.WINANSI, false );
directcontent = writer.getDirectContent();
directcontent.beginText();
directcontent.setFontAndSize(bf, 12);
directcontent.showTextAligned( Element.ALIGN_LEFT, "Married?", 36, 770, 0 );
directcontent.showTextAligned( Element.ALIGN_LEFT, "YES", 58, 750, 0);
directcontent.showTextAligned( Element.ALIGN_LEFT, "NO", 102, 750, 0);
directcontent.showTextAligned( Element.ALIGN_LEFT, "Name partner?", 36, 730, 0 );
directcontent.endText();
// initialize reusable objects
Rectangle = createObject("java", "com.lowagie.text.Rectangle");
PdfFormField = createObject("java", "com.lowagie.text.pdf.PdfFormField");
RadioCheckField = createObject("java", "com.lowagie.text.pdf.RadioCheckField");
TextField = createObject("java", "com.lowagie.text.pdf.TextField");
Color = createObject("java", "java.awt.Color");
married = PdfFormField.createRadioButton(writer, true);
married.setFieldName("married");
writer.addAnnotation( married );
// Note: Field names changed to avoid CF reserved word conflicts (ie "yes", "no")
married.setValueAsName("yes");
rectYes = Rectangle.init( 40, 766, 56, 744 );
yesField = RadioCheckField.init(writer, rectYes, javacast("null", ""), "yes");
yesField.setChecked(true);
married.addKid( yesField.getRadioField() );
rectNo = Rectangle.init( 84, 766, 100, 744 );
noField = RadioCheckField.init(writer, rectNo, javacast("null", ""), "no");
noField.setChecked(false);
married.addKid( noField.getRadioField() );
writer.addAnnotation( married );
rect = Rectangle.init( 40, 710, 200, 726 );
partner = TextField.init( writer, rect, "partner" );
partner.setBorderColor( Color.BLACK );
partner.setBorderWidth( 0.5 );
writer.addAnnotation( partner.getTextField() );
document.close();
WriteOutput("File created! "& outputPath &"<hr>");
}
// cleanup
finally {
if (isDefined("document")) {
document.close();
}
if (isDefined("stream")) {
stream.close();
}
}
</cfscript>
2) Add the Javascript (CF9 Only)
<cfscript>
inputPath = ExpandPath("form_without_js.pdf");
outputPath = ExpandPath("form_plus_js.pdf");
document = createObject("java", "com.lowagie.text.Document").init();
try {
// read in the pdf form
reader = createObject("java", "com.lowagie.text.pdf.PdfReader").init( inputPath );
stream = createObject("java", "java.io.FileOutputStream").init( outputPath );
stamper = createObject("java", "com.lowagie.text.pdf.PdfStamper").init( reader, stream );
// add javascript functions to the document
stamper.getWriter().addJavaScript( jsCode );
// create reference objects
PdfName = createObject("java", "com.lowagie.text.pdf.PdfName");
PdfAction = createObject("java", "com.lowagie.text.pdf.PdfAction");
PdfDictionary = createObject("java", "com.lowagie.text.pdf.PdfDictionary");
PushbuttonField = createObject("java", "com.lowagie.text.pdf.PushbuttonField");
// extract the parent option from the form
formObj = stamper.getAcroFields();
fd = formObj.getFieldItem("married");
// retrieve the dictionaries for "yes" radio button
// note: CF9 iText version does not have the getWidgetRef(index) method
dictYes = reader.getPdfObject( fd.widget_refs.get(0) );
yesAction = dictYes.getAsDict( PdfName.AA );
if (not IsDefined("yesAction")) {
yesAction = PdfDictionary.init();
}
// add an onFocus event to this field
yesAction.put( PdfName.init("Fo"), PdfAction.javaScript("setReadOnly(false);", stamper.getWriter()));
dictYes.put( PdfName.AA, yesAction );
// retrieve the dictionaries for "no" radio button
dictNo = reader.getPdfObject( fd.widget_refs.get(1));
noAction = dictNo.getAsDict( PdfName.AA );
if (not IsDefined("noAction")) {
noAction = PdfDictionary.init();
}
// add an onFocus event to this field
noAction.put( PdfName.init("Fo"), PdfAction.javaScript("setReadOnly(true);", stamper.getWriter()));
dictNo.put(PdfName.AA, noAction);
// create a submit button
writer = stamper.getWriter();
button = PushbuttonField.init( writer, Rectangle.init(40, 690, 200, 710), "submit" );
button.setText( "validate and submit" );
button.setOptions( PushbuttonField.VISIBLE_BUT_DOES_NOT_PRINT );
validateAndSubmit = button.getField();
// this will call the validate function when the button is clicked
validateAndSubmit.setAction( PdfAction.javaScript("validate();", stamper.getWriter()) );
// add the button to page 1
stamper.addAnnotation(validateAndSubmit, 1);
WriteOutput("File created! "& outputPath &"<hr>");
}
finally {
// cleanup
if (isDefined("stamper")) {
stamper.close();
}
if (isDefined("stream")) {
stream.close();
}
}
</cfscript>
2) Add the Javascript (CF8 Only)
<cfscript>
inputPath = ExpandPath("form_without_js.pdf");
outputPath = ExpandPath("form_plus_js.pdf");
document = createObject("java", "com.lowagie.text.Document").init();
try {
// read in the pdf form
reader = createObject("java", "com.lowagie.text.pdf.PdfReader").init( inputPath );
stream = createObject("java", "java.io.FileOutputStream").init( outputPath );
stamper = createObject("java", "com.lowagie.text.pdf.PdfStamper").init( reader, stream );
// add javascript functions to the document
stamper.getWriter().addJavaScript( jsCode );
// create reference objects
PdfName = createObject("java", "com.lowagie.text.pdf.PdfName");
PdfAction = createObject("java", "com.lowagie.text.pdf.PdfAction");
PdfDictionary = createObject("java", "com.lowagie.text.pdf.PdfDictionary");
PushbuttonField = createObject("java", "com.lowagie.text.pdf.PushbuttonField");
// prepare to extract form field objects
formObj = stamper.getAcroFields();
// get parent option
fd = formObj.getFieldItem("married");
// retrieve the dictionaries for "yes" radio button
// note: CF8 iText version does not have the getWidgetRef(index) method or getDirectObject()
dictYes = reader.getPdfObject( fd.widget_refs.get(0) );
yesAction = reader.getPdfObject(dictYes.get(PdfName.AA));
if (not IsDefined("yesAction") or not yesAction.isDictionary()) {
yesAction = PdfDictionary.init();
}
// add an onFocus event to this field
yesAction.put( PdfName.init("Fo"), PdfAction.javaScript("setReadOnly(false);", stamper.getWriter()));
dictYes.put( PdfName.AA, yesAction );
// retrieve the dictionaries for "no" radio button
dictNo = reader.getPdfObject( fd.widget_refs.get(1));
noAction = reader.getPdfObject( dictNo.get(PdfName.AA) );
if (not IsDefined("noAction") or not noAction.isDictionary()) {
noAction = PdfDictionary.init();
}
// add an onFocus event to this field
noAction.put( PdfName.init("Fo"), PdfAction.javaScript("setReadOnly(true);", stamper.getWriter()));
dictNo.put(PdfName.AA, noAction);
// create a submit button
writer = stamper.getWriter();
button = PushbuttonField.init( writer,
Rectangle.init(40, 690, 200, 710),
"submit"
);
button.setText( "validate and submit" );
button.setOptions( PushbuttonField.VISIBLE_BUT_DOES_NOT_PRINT );
validateAndSubmit = button.getField();
// this will cal the validate function when the button is clicked
validateAndSubmit.setAction( PdfAction.javaScript("validate();", stamper.getWriter()) );
// add the button to page 1
stamper.addAnnotation(validateAndSubmit, 1);
WriteOutput("File created! "& outputPath &"<hr>");
}
catch(Any e) {
WriteOutput("ERROR: "& e.message);
}
if (isDefined("stamper")) {
stamper.close();
}
if (isDefined("stream")) {
stream.close();
}
</cfscript>
4 comments:
Hi,
Using this approach - would it be possible to add javascript to an existing PDF that allowed two buttons e.g.
1) Submitted the form to server without calling the validation on required fields.
2) Validated the form only according to required fields.
Ideally, I would like to have a PDF form in the browser which can be saved at any point to allow the user to work on it until they are ready to submit. On final submission it would validate (using the validation added to the form in pdf form designer).
Thanks
Yes, items #1 and #2 are possible. That is similar to what the example above does. ie Check condition X and do Y if some condition, otherwise do something else.
However, saving the form (with data) requires that extended reader rights be enabled for the form. That can only be done with Adobe software.
-Leigh
Hi
I would be displaying the form in the browser using CF. I would want to allow submission to the server without applying the validation in the form (required fields created in livecycle designer).
This would allow the user to save a partial form.
Is this possible?
Thanks
If you are working with LC (LiveCycle) forms, that is whole 'nother kettle fish. LC forms have a totally different structure than AcroForms. I do not use LC, but I believe it also has its own javascript API. So you will have to check your documentation on that one.
-Leigh
Post a Comment