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

提交和处理 PDF 表单数据

starIconstarIconemptyStarIconemptyStarIconemptyStarIcon

2.00/5 (1投票)

2016年1月3日

CPOL

5分钟阅读

viewsIcon

42679

downloadIcon

733

本文解释了 PDF 表单的基础知识。特别是,它提供了在 ASP.NET MVC 应用程序中处理 PDF 表单数据的示例代码。

如果您必须在 HTML 表单和 PDF 表单之间进行选择 - 或者可能需要同时支持两者 - 那么了解这两种表单之间的区别以及它们的共同之处是很有好处的。本文的范围仅限于经典的 PDF 表单 - 而不是 XFA 表单。

HTML 表单

HTML 表单看起来像这样

<h1>I want pizza!</h1>
<form method="get" action="order">
   <p>Choose a size:</p>
   <input type="radio" name="size" value="small">small<br>
   <input type="radio" name="size" value="medium">medium<br>
   <input type="radio" name="size" value="large">large
   <p>Choose ingredients:</p>
   <input type="checkbox" name="tomatoes" value="tomatoes">tomatoes<br>
   <input type="checkbox" name="onions" value="onions">onions<br>
   <input type="checkbox" name="tuna" value="tuna">tuna<br>
   <input type="checkbox" name="cheese" value="cheese">cheese
   <p>My name:</p>
   <input type="text" name="name" /><input type="submit" value="order" />
</form>

字段本身及其外观都由同一个元素表示,即 input 元素。另一方面,PDF 完全分离了字段与其表示的概念。

PDF 表单

PDF 字段在文档级别定义,每个字段可以有零个或多个称为 _小部件_ (widgets) 的视觉表示。每个小部件都与一个页面相关联。下图显示了这种结构以及文档、页面、字段和小部件之间的关系。

在本文中,我将使用以下 PDF 表单。

我使用记事本创建了文本部分,然后将其打印成 PDF。接下来,我使用 Adobe Acrobat Pro DC 添加了表单元素。大小选项是共享同一组名称“size”的单选按钮。“small”、“medium”和“large”单选按钮是同一组名为“size”的组成部分。配料是带有相应名称的复选框。最后,有一个名为“name”的文本框和一个名为“order”的按钮。

向 PDF 添加提交按钮

要将 PDF 表单提交到 Web 端点,您需要添加一个具有 _提交表单操作_ 的按钮。您通常在 Adobe Acrobat 中进行此操作。这是添加提交表单操作后按钮属性对话框的操作选项卡的外观。

如果您选择该操作并单击“编辑”按钮,您将看到提交表单数据可用选项。

所选导出格式为 HTML。单击按钮时,这将把所有表单数据 POST 到指定的 URL。请注意,与 HTML 表单不同,无法指定 GET 作为 HTTP 方法。稍后我们将看到如何在 ASP.NET MVC 应用程序中处理此请求。

打开 PDF 表单

打开 PDF 表单似乎很简单,但事实并非如此。让我们在浏览器中打开此表单,看看会发生什么。您可以在此处打开它: http://www.tallcomponents.com/demos/pizza/form

作为实现说明,该表单位于 MVC 应用程序的 Content 文件夹内,其操作方法如下。

public class PizzaController : Controller
{
   public ActionResult Form()
   {
      return File("~/Content/order-pizza.pdf", "application/pdf");
   }
}

您的浏览器很有可能会直接渲染 PDF 表单本身,而不是使用 Adobe Reader 插件。Google Chrome 将 PDF 渲染为 HTML,并破坏了大量 PDF 功能,包括提交表单数据。Edge 也是如此。事实上,所有现代 Web 浏览器都已停止支持 Adobe Reader 插件所依赖的 NPAPI 插件基础架构。如果您在浏览器中单击“order”按钮,_将不会发生任何事情_。

这就是为什么 Adobe 使使用最新版本的 Adobe Reader 提交表单数据成为可能的原因。早期版本的 Adobe Reader 不允许这样做,除非您的文档经过 Reader 扩展。 (如果您确切知道此更改何时进入 Adobe Reader,请留下评论。我尝试过谷歌搜索但未成功。)

要在使用 Web 打开 PDF 文档或表单时获得完整的 PDF 体验,您必须禁用浏览器的 PDF 查看器。以下是 Google Chrome 的步骤。

  1. 浏览到 chrome://plugins
  2. 单击 Chrome PDF Viewer 的禁用链接。

(为其他浏览器搜索类似的说明。)

如果您现在使用相同的链接在浏览器中打开表单,您的默认系统 PDF 查看器(确保它是 Adobe Reader)会在_浏览器外部_打开 PDF,如下所示。

从 Adobe Reader 提交表单数据

从 Adobe Reader 单击“order”按钮会将表单数据提交到端点 _http://www.tallcomponents.com/demos/pizza/order_。这是处理此请求的 ASP.NET MVC 控制器操作。

public class PizzaController : Controller
{
   [HttpPost]
   public ActionResult Order(Pizza pizza)
   {
      return View(pizza);
   }
}

模型 Pizza

public class Pizza
{
   public string Size { get; set; }
   public string Tomatoes { get; set; }
   public string Onions { get; set; }
   public string Tuna { get; set; }
   public string Cheese { get; set; }
   public string Name { get; set; }
}

视图 Order.cshtml

@model Pizza
<h2>Hi @Model.Name!</h2>
<p>
   Thanks for ordering a @Model.Size pizza.
   Tomatoes: @Model.Tomatoes.
   Onions: @Model.Onions.
   Tuna: @Model.Tuna.
   Cheese: @Model.Cheese.
</p>

注意 MVC 如何根据名称自动将表单数据映射到 Pizza 的成员。

单击“order”按钮后,将显示以下对话框。

单击“允许”后,Adobe Reader 会请求打开响应的权限。

显然,Adobe Reader 将响应保存到临时位置。单击“是”后,默认浏览器会显示响应。

这是符合预期的,但用户体验远非最佳。

返回 PDF 响应

上一个用例返回 HTML 作为响应。因此,浏览器实例会打开并显示 HTML。让我们看看如果我们返回 PDF 作为响应会发生什么。

我创建了订单披萨表单的_第二个_版本,您可以在此处打开: http://www.tallcomponents.com/demos/pizza/form2

此 PDF 的“order”按钮将数据提交到_第二个_端点,该端点使用 PDFKit.NET 按如下方式返回 PDF 响应:

[HttpPost]
public ActionResult Order2(Pizza pizza)
{
   Document document = new Document();
   Page page = new Page(PageSize.Letter);
   document.Pages.Add(page);  
   double margin = 72; // points
   MultilineTextShape text = new MultilineTextShape(
      margin, page.Height - margin, page.Width - 2 * margin);
   page.Overlay.Add(text);
   Fragment fragment = new Fragment(
      string.Format("Hi {0}!, thanks for ordering a {1} pizza!", 
         pizza.Name, pizza.Size),
      Font.Helvetica,
      16);
   text.Fragments.Add(fragment);
   // send to browser
   Response.ContentType = "application/pdf";
   Response.AppendHeader("Content-disposition", "attachment; filename=file.pdf");
   document.Write(Response.OutputStream);
   return null;
}

如果我现在单击“order”按钮,将打开一个新的实例显示以下响应。

返回已展平的 PDF 表单

当 PDF 表单中的所有字段都已被替换为对应于表单数据的不可编辑图形时,该 PDF 表单被称为_已展平_ (flattened)。请注意,字段不仅仅是被禁用或设为只读,而是已被完全移除,并被非交互式内容所取代。让我们看看如何返回展平的表单作为响应。

我创建了订单披萨表单的_第三个_版本,您可以在此处打开: http://www.tallcomponents.com/demos/pizza/form3

此 PDF 的“order”按钮将数据提交到_第三个_端点,该端点使用 PDFKit.NET 将提交的数据与原始表单合并并按如下方式展平表单:

[HttpPost]
public ActionResult Order3(Pizza pizza)
{
  using (FileStream file = new FileStream(
    Server.MapPath("~/Content/order-pizza3.pdf"),
    FileMode.Open, FileAccess.Read))
  {
    // import submitted data into original form
    Document document = new Document(file);
    FormData data = FormData.Create(System.Web.HttpContext.Current.Request);
    document.Import(data); 
    // flatten form
    foreach (Field field in document.Fields)
    {
      foreach (Widget widget in field.Widgets)
      {
        widget.Persistency = WidgetPersistency.Flatten;
      }
    }
    // send to browser
    Response.ContentType = "application/pdf";
    Response.AppendHeader("Content-disposition", "inline; filename=file.pdf");
    document.Write(Response.OutputStream);
    return null;
  }
}

如果我现在单击“order”按钮,将打开一个新的 Adobe Reader 实例显示以下响应。

如果您尝试单击字段并更改值,您会发现什么都没有发生。如果您保存响应并使用 Adobe Acrobat 打开 PDF,您会发现没有字段。

© . All rights reserved.