65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ASP.NET MVC 和 Open Office 生成弹出 PDF 表单

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (7投票s)

2010年6月10日

CPOL

4分钟阅读

viewsIcon

66255

downloadIcon

3204

使用 Open Office 和 Sun PDF Import 插件创建 PDF 表单并在 ASP.NET MVC 中显示

引言

此演示展示了如何在 ASP.NET MVC 中生成和填充弹出 PDF 表单。生成的 PDF 表单将在新窗口中打开,但错误将显示在父窗口中。该演示还展示了如何使用免费的开源工具(如 Open Office 3.2 和 iTextSharp)创建和生成 PDF 表单,因此您不需要 Acrobat Pro 等付费工具。

设置您的环境

如果您没有 Acrobat Pro 来创建 PDF 表单,此演示将向您展示如何使用 Open Office 3.2 和 Sun PDF Import 扩展。

此演示中包含的内容

演示项目包括 iTextSharp 5.0.2.0 的免费开源 DLL,以及 jquery 1.3.2 和 query 插件的 JS 文件。

使用 OpenOffice 创建 PDF 表单

如果您没有 Acrobat Pro 来创建 PDF 表单,您可以使用免费的开源 Open Office 和 PDF Import 插件。

  1. 打开 OpenOffice Draw,创建您的表单。您还可以使用 PDF Import 插件导入现有的 PDF。
  2. 如果看不到“表单控件”工具栏,请转到“视图”-->“工具栏”-->“表单控件”将其添加。
  3. 对于您想要在表单中填充的每个字段,
    1. 在“表单控件”工具栏上选择“文本框”按钮,然后将其绘制在表单上。
    2. 选择该“文本框”,然后按“表单控件”工具栏上的“控件”按钮。格式化“文本框”
      1. 将边框更改为“无边框”,以便在生成时与表单融合。
      2. 将“文本框”的名称更改为有意义的名称。
      3. 将对齐方式更改为居中。
      4. 调整字体大小、粗细等。
  4. 保存文件,然后通过转到“文件”-->“导出为 PDF”将其导出为 PDF。
    1. 确保选择了“创建 PDF 表单”常规选项,并将“提交格式”设置为 PDF。
  5. 刷新您的“表单”文件夹,并将这两个文件添加到您的项目中。
  6. 如果您稍后想更改表单,则需要保留 ODG 文件,因为导出的 PDF 在导入回 Open Office 时会丢失“文本框”。如果您稍后打开 ODG 文件进行更改,请务必按“表单控件”工具栏上的“设计模式开/关”按钮,以在布局中查看和选择文本框。

创建 Controller GET 操作

在 `HomeController` 中,您将看到 `MyFormLetter` 的 `get` 和 `post` 操作。在 `get` 操作中,您将看到一个默认业务对象被创建并传递给视图。

[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);
}

创建 View

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
    Inherits="System.Web.Mvc.ViewPage" %>

表单操作指定了 post 操作,并在新浏览器窗口中打开 PDF。

<form action="<%= Url.Action("MyFormLetter", "Home") %>" method="post"
        target="_blank"><!-- popup pdf to new window --> 

输入字段会使用 `get` 操作中设置的业务对象属性进行预填充。

<input id="Company" name="Company" value="<%= Model.Company %>" /> 

创建 Controller Post 操作

`MyFormLetter` 视图会将用户填充的业务对象发布到 `MyFormLetter` post 操作。业务对象会根据业务逻辑进行修改,并填充 PDF 表单字段。PDF 文件由 `GetPdfFileStreamResult` 方法加载、生成和填充。如果发生错误,错误将返回到父页面,弹出表单将关闭。

/// <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);
            }
}  

为表单名称设置别名

当用户保存 PDF 文档时,文件的默认名称将设置为控制器操作名称。如果我们不想让用户看到“MyFormLetter.pdf”,而是希望他们看到“YourFormLetter.pdf”,我们可以简单地将 `FileStreamResult` 传递给一个名为 `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"];
} 

生成和填充 PDF 表单

`GetPdfFileStreamResult` 和 `GeneratePdf private` 方法从服务器加载 PDF 表单文件,并使用 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;
}

将错误发送回父窗口

由于我们不希望错误显示在弹出窗口中,而是希望它们显示在父窗口中,然后关闭弹出窗口。错误会传递给 `TempData`,并调用 `ParentError` 操作。

[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();
}

ParentError.aspx 视图使用 JavaScript 将错误传递回父窗口并关闭弹出窗口。如果您只调用 `window.close`,Internet Explorer 会提示您是否允许关闭页面。使用下面的代码,页面会在 Internet Explorer 和 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();
} 

请注意,Error.html 页面位于Public文件夹中,而不是任何Views文件夹中。这是因为它旨在处理应用程序中发生的任何错误,包括登录错误,因此即使用户未授权,也会显示错误。

错误通过 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>

试一试

按 Ctrl-F5 查看 My Form Letter 页面。输入一个姓名,提交表单,然后在新浏览器窗口中查看 PDF 弹出窗口。要测试错误处理,请取消注释 `MyFormLetter` 控制器操作中的 `throw new NullReferenceException("OH NOOOOOO")` 行,按 Ctrl-F5,然后按提交。

历史

  • 2010年6月9日:初始发布
© . All rights reserved.