关于在.net Core使用iTextsharp导出pdf

前言:这两个星期一直在弄在Core上导出pdf文件,并且pdf文件是属于比较复杂,所以需要用到模板来填充数据,遇到了很多的问题!

  • 使用的是Core,iTextsharp最新的版本5.x与之不兼容,并且最新的版本已经不开源了,商业用途需要收费的。
  • 我使用的是版本4.x,还是零几年的一个版本。这个版本有一个问题就是凡是编码的字体都会嵌入到pdf中,所以一个原本只有几十k大小的pdf变成了4m多,而且这只是一个pdf,如果有批量导出的话,五十个就是几百m了,可怕!
  • 最关键的是所有的注释全部都是英文,而且是一段落一段落的,而且相关的问题百度到的压根没有参考价值,只能去stackoverflow找答案,看的脑子都浆糊了...

所以在此将我所遇到的一些坑记录一下,防止有人重走我的老路!


详细步骤

pdf模板制作

下载adobe acrobat pro这个软件,当然是收费么,不过我大天朝嘛(ps:大家有钱的还是要支持支持正版呀,当然我很穷。)然后打开一个pdf文档,点击导航栏的表单,可以看到有很多选项:表单向导是自动帮你选择区域(将空白的部分划分成一个区域),你也可以自己添加区域来编辑。类似下图,那一个一个蓝色的就是一个一个区域,回头可以直接赋值的

模板区域制作

pdf模板赋值

  • 首先需要导入iTextsharp这个库,如果你不是Core的话直接导入最新版本就好了,如果你用Core的话我在里面貌似没找到5.x之前的版本,我导的是iTextSharp.LGPLv2.Core,这是伊朗的一位C#开发者封装的一个工具类。

  • 导入库之后就可以来对模板进行赋值,代码如下

     //获取中文字体,第三个参数表示为是否潜入字体,但只要是编码字体就都会嵌入。
     BaseFont baseFont = BaseFont.CreateFont(@"C:\Windows\Fonts\simsun.ttc,1",BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);
     //读取模板文件
     PdfReader reader = new PdfReader(@"C:\Users\Administrator\Desktop\template.pdf");
    
     //创建文件流用来保存填充模板后的文件
     MemoryStream stream = new MemoryStream();
             
     PdfStamper stamp = new PdfStamper(reader, stream);
     //设置表单字体,在高版本有用,高版本加入这句话就不会插入字体,低版本无用
     //stamp.AcroFields.AddSubstitutionFont(baseFont);
    
     AcroFields form = stamp.AcroFields;
    
     //表单文本框是否锁定
     stamp.FormFlattening = true;
    
     //填充表单,para为表单的一个(属性-值)字典
     foreach (KeyValuePair<string, string> parameter in para)
     {
         //要输入中文就要设置域的字体;
         form.SetFieldProperty(parameter.Key, "textfont", baseFont, null);
         //为需要赋值的域设置值;
          form.SetField(parameter.Key, parameter.Value);
      }
    

    还可以对模板添加图片,代码如下

    // 通过域名获取所在页和坐标,左下角为起点
    float[] positions = form.GetFieldPositions("sender");
    int pageNo = (int)positions[0];
    float x = positions[1];
    float y = positions[2];
    // 读图片
    Image image = Image.GetInstance(@"C:\Users\Administrator\Desktop\02.png");
    // 获取操作的页面
    PdfContentByte under = stamp.GetOverContent(pageNo);
    // 根据域的大小缩放图片
    image.ScaleToFit(positions[3] - positions[1], positions[4] - positions[2]);
    // 添加图片
    image.SetAbsolutePosition(x, y);
    under.AddImage(image);
    
  这里大家可能对**positions**不太了解,他的注释:

>Gets the field box positions in the document. The return >is an array of float multiple of 5. For each of this groups the values are: [page, llx, lly, urx, ury]. The coordinates have the page rotation in consideration.


大概意思就是获取pdf一个域的坐标,返回了一个数组,盛放的分别是:[当前是第几页],[区域左下方的X坐标],[区域左下方的Y坐标],[区域右上方的X坐标],[区域右上方的Y坐标]。通过四个点就知道这块区域的大小,然后再定位到是第几页。(语文水平不太高,听不懂就.....see more times)



最后按顺序关闭io流

    stamp.Close();
    reader.Close();

这里是单个模板赋值,单个模板生成多个pdf就加个for循环就好,多个模板的话也差不多,单个模板搞定了,其他都简单了


合并生成的多个pdf文件

在上面我这里没有生成pdf文件,而是用MemoryStream将赋值完成的pdf放到了内存中,然后再将他们合并发送到客户端直接下载,这样就避免了许多的文件io操作,提升点效率。(当然如果文件太多太大的话可能会内存溢出而挂掉吧)

代码如下:

Document document = new Document();

//将合并后的pdf依旧以流的方式保存在内存中
MemoryStream mergePdfStream = new MemoryStream();

//这里用的是smartCopy,整篇文档只会导入一份字体。属于可接受范围内
PdfSmartCopy copy = new PdfSmartCopy(document, mergePdfStream);

document.Open();

for (int i = 0; i < pdfStreams.Count; i++)
{
   for循环读取之前生成的单个pdf字节数组,这里将流保存在数组中传递过来使用的话不行,会报错,我也不知道为什么,所以用了字节数组(如果有人知道的话还请告诉我,不胜感激)
   byte[] stream = pdfStreams[i];
   PdfReader reader = new PdfReader(stream);

   //for循环新增文档页数,并copy pdf数据
   document.NewPage();
   PdfImportedPage imported = copy.GetImportedPage(reader, 1);
   copy.AddPage(imported);

    reader.Close();
 }
 copy.Close();
 document.Close();

 //这里将合并后的pdf流传出去,方法我没全复制过来。(你们也不能光复制就想着直接用吧,好歹动下头脑)
 return mergePdfStream;

pdf在浏览器中下载

这是最后一步了,我用的是MVC,不过不管用什么都一样的原理

FileResult fileResult = new FileContentResult(mergePdfStream.ToArray(), "application/pdf");
fileResult.FileDownloadName = "4.pdf";

然后直接返回fileResult就行,因为它是继承于ActionResult的,应该是MVC框架的一个专门返回文件的类。(.net就是爽,微软封装的太好了)


and最终

趁还在,多陪陪身边的人,莫等到失去才开始后悔。

另外厦门是个好地方!


联系方式

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容