Generate Popup PDF Forms with ASP.NET MVC and Open Office






4.50/5 (7 votes)
Use Open Office and Sun PDF Import plugin to create PDF forms and display with ASP.NET MVC
Introduction
This demo shows you how to generate and populate a popup PDF form in ASP.NET MVC. The generated PDF form is opened in a new window, but errors are shown in the parent window. The demo also shows you how to create and generate PDF forms using free open source tools such as Open Office 3.2 and iTextSharp, therefore you do not need non-free tools such as Acrobat Pro.
Setup your Environment
If you don't have Acrobat Pro to create PDF forms, this demo shows you how to use Open Office 3.2 and the Sun PDF Import extension.
- Download and install Open Office (it’s free and open source): http://download.openoffice.org
- Download and install the free PDF import Open Office extension from Sun: http://extensions.services.openoffice.org/project/pdfimport
Included in this Demo
The demo project includes the free open source DLL for iTextSharp 5.0.2.0 and the JS files for jquery 1.3.2 and the query plugin.
Create the PDF Form using OpenOffice
If you don’t have Acrobat Pro to create PDF forms, you can use the free open source Open Office and PDF Import plugin.
- Open OpenOffice Draw, create your form. You can also import an existing PDF using the PDF Import plugin.
- If you don’t see the Form Controls toolbar, add it by going to View --> Tool Bars --> Form Controls.
- For each field you want to populate in the form,
- Select the
TextBox
button on the Form Controls toolbar and draw it on your form. - Select that
textbox
and press the Control button on the Form Controls toolbar. Format thetextbox
:- Change the Border to “Without frame” so it blends into the form when generated
- Change the name of
textbox
to something meaningful - Change the alignment to centered
- Adjust the font size, weight, etc.
- Select the
- Save the file and export it to PDF by going to File --> Export As PDF.
- Be sure the “Create PDF form” general option is selected and “Submit format” is set to PDF.
- Refresh your Forms folder and add both files to your project.
- You’ll need to keep the ODG file if you want to make changes to the form later because the PDF you export loses the
textbox
es when you import it back into Open Office. If you open the ODG file later to make changes, be sure to press the Design Mode On/Off button on the Form Controls toolbar to see and select the textboxes in your layout.
Create Controller GET Action
In HomeController
, you will see the get
and post
actions for MyFormLetter
. In the get
action, you will see a default business object is created and passed to the view.
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult MyFormLetter()
{
// by setting the Company property on the model,
// the form will be filled out with the company name,
// but not the applicant name
Applicant defaultApplicant = new Applicant()
{
Company = "My Company"
};
return View(defaultApplicant);
}
Create the View
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage " %>
The form action specifies the post action and opens the PDF in a new browser window.
<form action="<%= Url.Action("MyFormLetter", "Home") %>" method="post"
target="_blank"><!-- popup pdf to new window -->
Input fields are pre-populated with the business object properties set in the get
action.
<input id="Company" name="Company" value="<%= Model.Company %>" />
Create Controller Post Action
The MyFormLetter
view posts to the MyFormLetter
post action, passing the user-populated business object. The business object is modified by any business logic and the PDF form fields are populated. The PDF file is loaded, generated and populated by the GetPdfFileStreamResult
method. If an error occurs, the error is sent back to the parent page and the popup form is closed.
/// <summary>
/// The MyFormLetter form posts to this action
/// </summary>
/// <param name="applicant"></param>
/// <returns></returns>
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult MyFormLetter(Applicant applicant)
{
try
{
// uncomment this to test error handling
// throw new NullReferenceException("OH NOOOOOO");
// this is where you would get further info by calling business logic,
// data access, etc.
applicant.Company = "My Company";
// populate the value of each form field in the pdf form
Dictionary<string,> formFields = new Dictionary<string,>
{
{"ApplicantName", applicant.Name},
{"CompanyName", applicant.Company}
};
string fileName = "MyFormLetter.pdf";
// we don't want the user to see "MyFormLetter.pdf"
// when they save the file,
// we want them to see "YourFormLetter.pdf" when they save the PDF
// so we alias the name simply by passing it to another action
// named YourFormLetter
// pass the file stream result to the alias action
TempData["MyFormLetter_FileStreamResult"] =
GetPdfFileStreamResult(fileName, formFields);
return RedirectToAction("YourFormLetter");
}
catch (Exception ex)
{
return HandleErrorForPopup(ex, applicant);
}
}
Alias the Form Name
When the user saves the PDF document, the default name of the file will be set to the controller action name. If we don’t want the user to see “MyFormLetter.pdf” and instead want them to see “YourFormLetter.pdf”, we can simply pass theFileStreamResult
to a new controller action called YourFormLetter
.
/// <summary>
/// When the user saves the pdf, the default name of the
/// file will be "YourFormLetter.pdf".
/// </summary>
/// <returns></returns>
public FileStreamResult YourFormLetter()
{
return (FileStreamResult) TempData["MyFormLetter_FileStreamResult"];
}
Generate and Populate the PDF Form
The GetPdfFileStreamResult
and GeneratePdf private
methods load the PDF form file from the server, and populate the form fields using iTextSharp.
private FileStreamResult GetPdfFileStreamResult
(string fileName, Dictionary<string,> formFields)
{
MemoryStream memoryStream = GeneratePdf(fileName, formFields);
// create a new return stream because the
// MemoryStream from the file is closed
MemoryStream returnStream = new MemoryStream();
returnStream.Write(memoryStream.GetBuffer(), 0,
memoryStream.GetBuffer().Length);
returnStream.Flush();
// rewind stream back to beginning so it can be rendered to the page
returnStream.Seek(0, SeekOrigin.Begin);
return new FileStreamResult(returnStream, "application/pdf");
}
private MemoryStream GeneratePdf(string fileName, Dictionary<string,> formFields)
{
string formFile = HttpContext.Server.MapPath("~/Forms/" + fileName);
PdfReader reader = new PdfReader(formFile);
MemoryStream memoryStream = new MemoryStream();
PdfStamper stamper = new PdfStamper(reader, memoryStream);
AcroFields fields = stamper.AcroFields;
// set form fields
foreach (KeyValuePair<string,> formField in formFields)
{
fields.SetField(formField.Key, formField.Value);
}
stamper.FormFlattening = true;
// release file
stamper.Close();
reader.Close();
return memoryStream;
}
Sending Errors Back to the Parent Window
Since we don’t want errors to appear in the popup window, we want them to appear in the parent window, then close the popup. Errors are passed to TempData
and the ParentError
action is called.
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult HandleErrorForPopup(Exception ex, object inputData)
{
//TODO: log and/or email error
// close the popup browser window and display the error in the parent window
TempData["ErrorMessage"] = ex.Message;
return RedirectToAction("ParentError");
}
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult ParentError()
{
return View();
}
The ParentError.aspx view uses JavaScript to pass errors back to the parent window and closes the popup. If you just call window.close
, Internet Explorer will prompt you asking if you want to allow the page to close. With the code below, the page will close without a prompt in both Internet Explorer and Firefox.
function HandleError() {
opener.window.location =
'/Public/Error.html?error=<%= TempData["ErrorMessage"].ToString() %>';
// close the form window without a prompt
window.open('', '_self');
window.close();
}
Notice that the Error.html page lives in the Public folder, not in any of the Views folder. This is because it is designed to handle errors that occur anywhere in the application, including login errors, so an error will be displayed even if the user is not authorized.
Errors are Displayed to the User using JQuery
<script src="/Content/jquery-1.3.2.min.js" type="text/javascript"></script>
<script src="/Content/jquery.query.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$("div#lblErrorMessage").html($.query.get("error"))
});
</script>
Try It Out
Hit Ctrl-F5 and see the My Form Letter page. Type in a name, submit the form, and see the PDF popup in a new browser window. To test error handling, uncomment the throw new NullReferenceException("OH NOOOOOO")
line in the MyFormLetter
controller action, hit Ctrl-F5, and press submit.
History
- 9th June, 2010: Initial post