创建/填写打印表单和PDF的最佳方式

本文关键字:PDF 最佳 方式 表单 打印 创建 | 更新日期: 2023-09-27 17:59:01

我们有一个C#应用程序,它必须打印复杂的表单。比如必须采用特定格式的多页政府合规表格。我们可以获得这些表格的PDF副本并创建表格字段,但不确定如何填写这些数据并创建可以自动打印并发送给我们客户的PDF(他们需要纸质副本)。

此外,有些表格是动态的,因为某些页面必须重复(例如,对于员工机会均等审计报告,如果表格中有50名员工,但客户有250名员工,我们可能需要在表格中包含5份页面副本)。

一般来说,填充和打印这些表格的最佳方式是什么?请注意,我们的应用程序是基于C#的,但任何语言/应用程序的任何解决方案都是受欢迎的(如果需要,我们愿意购买软件或与其他框架集成)。

例如,像TurboTax这样的公司会用什么来打印它处理的数百份纳税申报表?

创建/填写打印表单和PDF的最佳方式

这里有几个选项。

1) FDF,表单数据格式。这是一个糟糕的规范文档,它只涵盖了FDF格式的一小部分(不常用、复杂)。FDF文件生成起来相当简单,包含一堆字段/值对(可以包含列表选项和其他不需要的高级内容)和文件关联。打开FDF将填充PDF(通过与acrobat/阅读器关联的文件)。

下面是一个示例(添加了额外的空白以使其更可读):

%FDF-1.2
1 0 obj
<< /FDF
  << /Fields  [
    << /V (Communications Co.)/T (Address1)>>
    << /V (29 Communications Road)/T (Address2)>>
    << /V (Busyville)/T (City)>>
    << /V (USA)/T (Country)>>
    << /V (24 January 2000)/T (Date)>>
    << /V (Suzie Smith)/T (Name)>>
    << /V ('(807') 221-9999)/T (PhoneNumber)>>
    << /V (777-11-8888)/T (SSN)>>
    << /V (NJ)/T (State)>>
  ]
  /F (TestForm.pdf)
  >>
>>
endobj
trailer
<<
  /Root 1 0 R
>>
%%EOF

"/V"表示字段值,"/T"是字段的标题。"/F"是指向要填写的表格的路径。

有许多邮件合并式的产品可以接收FDF和PDF,并生成一个完整的PDF表单。前面提到的iText(和其他一些)可以通过编程实现这一点,其他应用程序也有命令行。

在这种环境中,任何可能需要重复的页面都应该是它自己的形式。合并表单可能非常困难。有几种方法,其中最简单的是"压平"字段,使其成为页面内容(线条艺术和文本)。。。那么你就不再真正合并PDF表单了。

当然,如果你能控制打印的顺序,你根本不需要合并表单。你可以按正确的顺序打开/打印它们。

我记得AcrobatPro的批处理命令可以导入FDF数据并进行打印。您所需要做的就是生成适当的FDF文件,这主要是琐碎的字符串构建。

使用FDF假设您已经制作了PDF表格,只是在等待填写。如果不是这样的话。。。

2) 以编程方式生成PDF表单。我使用iText(iTextSharp的Java基础)来实现这一点,尽管有很多不同语言的库可用。iText[Sharp]根据AGPL(或商业)获得许可。使用AGPL,任何能够访问OUTPUT的人都必须能够访问应用程序的源。AGPL和普通的GPL一样具有"病毒性"。MPL下提供了较旧的版本。

考虑到这完全是内部的,而且你将打印PDF,许可证并不是什么大问题。

生成一次表单模板,然后直接或通过FDF填写,效率会高得多。

您可以使用最近创建的Kevsoft.PDFtk包来封装PDFtk服务器。

var pdftk = new PDFtk();
var fieldData = new Dictionary<string, string>()
{
   ["Best Coding Website Box"] = "Stackoverflow",
   ["My Check Box"] = "Yes"
};
var result = await pdftk.FillFormAsync(
   pdfFile: await File.ReadAllBytesAsync("myForm.pdf"),
   fieldData: fieldData,
   flatten: false,
   dropXfa: false
);
if(result.Success)
{
    await File.WriteAllBytes($"{Guid.NewGuid()}.pdf", result.Result);
}

GitHub页面上有更多相同的示例-https://github.com/kevbite/Kevsoft.PDFtk

此外,在这篇博客文章中还有额外的信息-https://kevsoft.net/2021/05/16/filling-pdf-forms-for-free-with-dotnet-core-and-net-5.html

Kevsoft.PDFtk根据MIT 获得许可

PDFtk服务器是根据GPLv2许可的,但是,如果您要打包要分发的应用程序,您可以购买重新分发许可证。

如果您的表单基于AcroForm技术:只需使用itext7即可完成此任务。通过在NuGet Package Manager控制台中执行以下命令将其添加到您的项目中:

安装软件包itext7

要编写特定的表单字段,请使用类似的代码:

PdfReader reader = new PdfReader(src);
PdfWriter writer = new PdfWriter(dest);
PdfDocument pdfDoc = new PdfDocument(reader, writer);
var form = PdfAcroForm.GetAcroForm(pdfDoc, true);
var fields = form.GetFormFields();
fields.Get(key).SetValue(value);
form.FlattenFields();
pdfDoc.Close();

在这个片段中,src是PDF文件的源,dest是生成PDF的路径。key与模板中某个字段的名称相对应。value与您要填写的值相对应。如果您希望表单保持其交互性,则需要删除form.flattenFields();,否则将删除所有表单字段,从而生成平面PDF。

注意

请注意,itext7是根据AGPL许可的,不是免费的商业用途或封闭源代码。(特别感谢@da_berni提供此必要信息)

您可以尝试Docotic.Pdf库。该库允许在现有文档中填写表单,导入和导出FDF数据,以及从头开始修改现有文档和创建表单。

几个样本:

如何填写现有表格

如何将FDF导入PDF文档

如何创建文本字段

Docotic.Pdf提供商业和免费许可证。

您的问题的部分答案是,您应该探索"ItextSharp"库,这是一个开源库,在创建PDF文件时很有用。

http://sourceforge.net/projects/itextsharp/

  1. 打印PDF表单(高质量)
  2. 扫描(高质量)
  3. 将扫描的文件转换为位图(*.dib)
  4. 使用Visual C(VS 2010,以编程方式)
    • 设置页面属性(即,准备页面、设备上下文等)
    • 创建您的字体(当然可以随心所欲)
    • 设置页面原点(页面顶部)
    • 加载并使用StretchDIBits(或类似功能)打印PDF页面图像
    • 转到页面顶部
    • 计算打印位置(pt.x和pt.y)
    • 使用pDC->TextOut(或其他任何您想要使用的)打印

以上内容适用于任何PDF页面上的任何数量的字段,但需要底层操作系统打印过程的一些基本知识。它可以让你完全控制页面,这意味着你可以划掉、打印等等……你可以做任何你喜欢的事情
我认为将其转换为VS 2017和C#没有任何问题。不需要库,只需要简单的老式手工编码。

我们使用aspose.words,我看到它们也有PDF API。

截至2020年FreeSpire.PDF看起来很有趣。从网站上引用https://www.e-iceblue.com/Introduce/free-pdf-component.html:

免费Spire.PDF for.NET是Spire.PDFfor.NET的社区版,这是一个完全免费的PDF API,用于商业和个人用途。作为一个独立的.NET库,Free Spire.PDF for.NET使开发人员能够在任何.NET(C#、VB.NET、ASP.NET、.NET Core)应用程序上创建、编写、编辑、转换、打印、处理和读取PDF文件

很明显,它最多限制在10页以内。

因为这里缺少它:也可以使用pdfSharp将值添加到字段中。要了解如何操作,请查看以下链接:填充表单字段和创建Acroform/add表单元素

尝试使用此:

string Filepath = Server.MapPath("/AOF.pdf");
var pdfpath = Path.Combine(Filepath, "");
var Formcontent = ListFieldNames(Filepath);
Formcontent["Name_txt"] =  "T.Test" ;  FillForm(Formcontent); //  var pdfContents = FillForm(pdfpath, Formcontent);
public Dictionary<string, string> ListFieldNames(string Filepath)
{
    //PdfReader pdfReader = new PdfReader(pdfTemplate);
    //StringBuilder sb = new StringBuilder();
    //foreach(DictionaryEntry de in pdfReader.AcroFields.Fields)
    //{
    //    sb.Append(de.Key.ToString() + Environment.NewLine);
    //}
    var Fileds = new Dictionary<string, string>();
    PdfReader pdfReader = new PdfReader(Filepath);
    var reader = new PdfReader(pdfReader);
    foreach (var entry in reader.AcroFields.Fields)
        Fileds.Add(entry.Key.ToString(), string.Empty);
    reader.Close();
    return Fileds;
}
public byte[] FillForm(string pdfPath, Dictionary<string, string> formFieldMap)
{
    var output = new MemoryStream();
    var reader = new PdfReader(pdfPath);
    var stamper = new PdfStamper(reader, output);
    var formFields = stamper.AcroFields;
    foreach (var fieldName in formFieldMap.Keys)
        formFields.SetField(fieldName, formFieldMap[fieldName]);
    stamper.FormFlattening = true;
    stamper.Close();
    reader.Close();
    return output.ToArray();
}
public void FillForm(Dictionary<string, string> Formfiledmap)
{
    string pdfTemplate = Server.MapPath("/AOF.pdf");
    string newFile = @"C:'Users'USer'Desktop'completed_fw4.pdf";
    PdfReader pdfReader = new PdfReader(pdfTemplate);
    PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(newFile, FileMode.Create));
    AcroFields pdfFormFields = pdfStamper.AcroFields;
    foreach (var fieldName in Formfiledmap.Keys)
        pdfFormFields.SetField(fieldName, Formfiledmap[fieldName]);
    pdfStamper.FormFlattening = true;
    pdfStamper.Close();
}