电子发票是电商时代的产物,PDF发票是最常见的电子发票之一。在这篇文章中,我将给大家分享一个免费的动态生成PDF电子发票的C#方案,并在文章末尾附上Java解决方案。

典型的发票包含客户和供应商的名称和地址、发票编号、购买物品的描述、付款金额等信息。为了动态地生成发票,我使用MS Word创建了一个模板,在该模板中设计好想要呈现的大部分内容及文档样式,然后通过代码替换文本和插入新内容,最后保存为PDF文档。用代码操作Word文档的部分需要使用免费版的Spire.Doc for .NET7.1。

创建模板

如图1所示,发票模板由两个表组成。表1用于显示买卖双方的信息和订单信息,表2用于陈列卖方向买方提供的产品或服务列表。我们需要做的是替换表1中以#开头的文本,并将客户的购物清单填充至第二个表格。

为了能自动计算总金额,需要在某些单元格添加公式域。例如,单元格E2包含公式“=C2*D2”,该公式将计算单元格B2中商品的总价。当买家购买超过一件商品时,我们需要在表格添加更多行并动态更新一些单元格的公式。

图1 发票模板

如何替换文本

Spire.Doc 有一个IBodyRegion.Replace(string given, string replace, bool caseSensitive, bool wholeWord)方法,可以用于替换文档中的指定字符串。例如,将“#orderNum”替换成“2516595027”,我们可以直接使用下面的代码,代码中doc是Document对象。

doc.Replace("#orderNum", "2516595027", true, true);

如何更新表格二

现在,让我们看看如何向现有表格添加行,如何将数据填充至表格,以及如何动态地更新公式。为了使逻辑更清晰,我创建了三个方法,并制作了图2,来展示它们的具体含义以及它们之间的调用关系。

图2 自定义方法的含义及调用关系

下面,看一下这三个方法的代码片段:

1. AddRows()

此方法实际上复制了现有表格的第二行,将复制行依次添加到第二行的下面。新行继承了第二行的单元格格式、字体样式和公式。因此,我们需要依次更新新行中的公式,以及下面随行数增加而变化的公式。

private static void AddRows(Table table, int rowNum)

{

for (int i = 0; i < rowNum; i++)

{

//将指定个数的第二行的复制行依次添加到第二行下面

table.Rows.Insert(2 + i, table.Rows[1].Clone());

//更新“金额”所对应单元格的公式

foreach (var item in table.Rows[2 + i].Cells[4].Paragraphs[0].ChildObjects)

{

if (item is Field)

{

Field field = item as Field;

field.Code = string.Format("=C{0}*D{0}\\# \"0.00\"", 3 + i);

}

break;

}

}

//更新“折扣金额”对应的单元格的公式

foreach (var item in table.Rows[4 + rowNum].Cells[4].Paragraphs[0].ChildObjects)

{

if (item is Field)

{

Field field = item as Field;

field.Code = string.Format("=E{0}*0.05\\# \"0.00\"", 3 + rowNum);

}

break;

}

//更新“总计”对应的单元格的公式

foreach (var item in table.Rows[5 + rowNum].Cells[4].Paragraphs[0].ChildObjects)

{

if (item is Field)

{

Field field = item as Field;

field.Code = string.Format("=E{0}-E{1}\\# \"¥#,##0.00\"", 3 + rowNum, 5 + rowNum);

}

break;

}

}

2. FillTableWithData()

此方法仅用于将sting[][]类型数据从表格的第二行开始写入表格。

private static void FillTableWithData(Table table, string[][] data)

{

for (int r = 0; r < data.Length; r++)

{

for (int c = 0; c < data[r].Length; c++)

{

//将数据从表格的第二行开始写入表格

table.Rows[r + 1].Cells[c].Paragraphs[0].Text = data[r][c];

}

}

}

3. WriteDataToDocument()

由于发票模板已经有一行(第二行)用于显示一项商品,因此我们需要判断是否需要添加更多行。如果客户只购买一项商品,模板文档就可以容纳商品信息并输出结果;否则,我们需要添加行来容纳更多的项目,并动态更新公式以获得正确的总金额。

private static void WriteDataToDocument(Document doc, string[][] purhcaseData)

{

//获取Word模板中的第二个表格

Table table = doc.Sections[0].Tables[1] as Table;

//若购买商品多于一项,则添加purhcaseData.Length - 1个行

if (purhcaseData.Length > 1)

{

AddRows(table, purhcaseData.Length - 1);

}

//将购买数据填充至表格

FillTableWithData(table, purhcaseData);

}

WriteDataToDocument()方法的参数之一是sting[][]对象,该对象存储了客户的购买信息,它的每个元素都是一个字符串数组,可以这样设置:

string[] product = new string[] { "1023", "华为 P30 Pro (8G+128G)全网通", "1", "4288" };

string[][]的长度则是商品的项数,如果长度大于1,则需要添加[长度 - 1]个新行。

生成发票

以下是Main函数中用于生成PDF发票的代码。

using Spire.Doc;

using Spire.Doc.Fields;

namespace CreatePdfInvoice

{

class Program

{

static void Main(string[] args)

{

//加载Word模板文档

Document doc = new Document();

doc.LoadFromFile("Invoice-Template.docx");

//替换文档中以#开头的文本

doc.Replace("#customerName", "小伟", true, true);

doc.Replace("#contactNum", "13601234567", true, true);

doc.Replace("#shippingAdd", "北京市海淀区幸福小区1幢2单元3号", true, true);

doc.Replace("#orderDate", "2019-05-30", true, true);

doc.Replace("#orderNum", "2516595027", true, true);

//定义客户购买数据

string[][] purchaseData = {

new string[]{"1023","华为 P30 Pro (8G+128G)全网通","1","4288"},

new string[]{"1429","华为Watch GT运动版","2","1288"},

new string[]{"1268","华为无线耳机 FreeBuds 2Pro","2","799"},

new string[]{"1281","华为 MateBook 14 (i5 8G 512G)","1","5999"},

};

//将购买数据写入模板文档的第二个表格

WriteDataToDocument(doc, purchaseData);

//更新域

doc.IsUpdateFields = true;

//保存为PDF格式文档

doc.SaveToFile("Invoice.pdf", FileFormat.PDF);

System.Diagnostics.Process.Start("Invoice.pdf");

}

}

}

生成的结果文档如下:

图 3 多项目PDF发票

如果你输入购买数据只有一行,那么你将得到如图4所示的结果文档。

string[][] purchaseData = {new string[]{"1023","华为 P30 Pro (8G+128G)全网通","1","4288"},};

图4 单项目PDF发票

工程下载

注:

免费版 Spire.Doc能加载和生成的Word文档不能超过500个段落或25个表格,将Word文档保存为PDF时仅支持前3页。绝大多数发票只有1页或2页,所以该方案适用于大多数情况。

java生成电子发票_C#/Java 动态生成电子发票相关推荐

  1. Java读取word模板,并动态生成word

    Java读取word模板,并动态生成word ​ 最近有个需求是将数据库里存入的用户个人信息生成一个word然后供用户下载,第一时间就就想到了poi来做,所以记录一下免得自己忘了,忘了也可以回来看看

  2. Java读取pdf模板,并动态生成pdf文件,如动态生成准考证

    Java读取pdf模板,并动态生成pdf文件,如动态生成准考证 ​ 前几天遇到了一个生成准考证的需求,并提供用户下载,然后百度了一圈还是觉得使用itextpdf这个框架好用点.但是还需要找到一个能创建 ...

  3. java jnlp_Java Web Start实践:动态生成JNLP

    Java很早就推出了Java Web Start(简称JWS)技术.这一技术的初衷很好:希望将桌面程序和Web页面之间搭起一个无缝的桥梁.虽然Applet技术已经存在了十多年,但是它 日趋老迈衰落,所 ...

  4. .NET不用代码生成器自己写一个生成Code的DLL 自动动态生成三层架构(一)概况...

    大家好,我是新灵感中的一个小灵感,今日有幸能在博客园写点自己的东西,非常的高兴,自己也从事这个职业差不多快4年了,一直没有怎么认真写过东西,非常的惭愧啊. 所以与其明天再写,还不如就从现在写起走,把自 ...

  5. html动态生成可输入的表格,动态生成表格.html

    动态生成表格 table, thead, tr, th, td{ border: 0; } th { width: auto; } 姓名科目成绩操作 // 使用对象储存的数据,列表 data = [ ...

  6. halcon中如何生成椭圆_教你动态生成椭圆,还教你怎么用海龟作图——GeoGebra制作教程...

    先看下面的动画演示,你能给出答案吗?海龟-文末有制作教程 这就涉及到椭圆是怎么来的! 什么是椭圆--将圆拉伸 / 压扁? 椭圆的定义是什么? 椭圆的定义平面内与两个定点F1,F2的距离的和等于常数(大 ...

  7. js前端动态生成变量及python后端动态生成变量接收

    前端代码: let sbmt = function(data){layui.use('layer', function() {var layer = layui.layer;$.ajax({url: ...

  8. java设置pdf不可编辑_Java动态生成pdf文件(使用itext编辑pdf)

    一.创建pdf模板 使用PDFelement制作pdf模板(数据域的名称对应后面插入的key) 二.导入maven依赖 com.itextpdf itextpdf 5.5.13 com.itextpd ...

  9. php动态生成链接,如何使用PHP动态生成HTML页面?

    我一直在做类似的工作,我有一些可能对你有帮助的代码.实例是here及以下,是我用来作为参考的代码. 创建-page.php文件 // Session is started. session_start ...

最新文章

  1. 线段树分治 ---- CF1217F - Forced Online Queries Problem(假离线 可撤销并查集 + 线段树分治)详解
  2. float gpu 加速_tensorflow - GPU 加速
  3. div 居中,浏览器兼容性
  4. windows server 网络负载均衡配置
  5. [IE 技巧] 显示/隐藏IE 的菜单/工具栏
  6. 统计字符串中单词个数的算法优化
  7. 读懂操作系统之虚拟内存(一)
  8. SCAN: Structure Correcting Adversarial Network for Organ Segmentation in Chest X-rays(译)
  9. 莫烦python学习笔记之numpy.array,dtype,empty,zeros,ones,arrange,linspace
  10. mysql创建用户navicat_14MYSQL创建用户和授权、15Navicat的使用、16-pymysql模块的使用、17-索引...
  11. 用自定义IHttpModule实现URL重写 1
  12. UE4 四叉树 QuadTree
  13. 耐得住寂寞方能不寂寞
  14. 程序员如何管理自己的代码
  15. Unity3D Shader 入门
  16. 九日集训 总结与展望
  17. 只用听的计算机课程录音,录制网络课程如何录声音?其实这个方法更简单
  18. FPGA产生相位编码基带信号
  19. 流行的jQuery信息提示插件(jQuery Tooltip Plugin)【转】
  20. 佘其炯:关于97工程的思考

热门文章

  1. win10 “你不能访问此共享文件夹,因为你组织的安全策略阻止未经身份验证的来宾访问。解决方案(实测有效)
  2. 深力科电子SND101系列兼容代替SLM561A 支持PWM调光 线性恒流LED驱动芯片
  3. H5鼠标拖动事件(drag)
  4. 从红魔7S系列看游戏手机的自驱进化
  5. 正则表达式教程及常用正则表达式
  6. idea 配置idk
  7. 《python语言程序设计》第2章第3题python 用class init 一起来做一个英尺变米的程序
  8. 5G手机的发射功率,到底能有多大?
  9. SpringBoot @RequestHeader注解接收请求头
  10. poi java 导出word_java poi 生成word文档并下载