Pdf文件处理组件对比(Aspose.Pdf,Spire.Pdf,iText7)-程序员宅基地

目的


 

因为公司是做医疗相关软件的,所以经常和文档打交道,其中就包含了Pdf。医院的Pdf(通常是他们的报告)都千奇百怪,而我们一直以来都是在用一些免费且可能已经没人维护了的组件来处理Pdf,所以就经常出现Pdf转乱码,甚至直接异常的情况。跟公司管理层反应了很久,终于答应掏腰包采购一款Pdf的处理组件,并且交由我来预研。稍微调查了一下后,最终商业组件选中了 Aspose,Spire还有Leadtool这三家公司的产品,另外由于iTextSharp作为开源的Pdf处理组件太有名,所以我也把它的重写版——iText7加入了对比的列表中,写了一个可以方便执行的Demo项目,想了想,为了那些同样需要Pdf相关资料的同学,也为了c# 系的开源生态,把这个项目特意整理了一下,发到了Github上,并且写了一些简单的使用说明。

 

GitHub的地址请戳 这里

概述


 

本项目主要以常见的五种Pdf处理来进行预研,包括“Pdf转Jpeg”,“Pdf转txt”,“Xps转Pdf”,“将jpeg作为插页插入到Pdf中”,“加水印”。

在对上面提出的四种组件进行充分调查后,直接否定了LeadTool,因为文档太少,转化质量也不行,官网下下来的Sample也乱得要死,所以本项目只包含了三个组件的实现 Aspose.Pdf,Spire.Pdf,iText7。

这里不得不提的就是iText虽然很有名,但功能还是不全的,至少我翻遍了文档也没找到把Pdf转图片和把Xps转Pdf这两个功能,如果有知道的同学麻烦留言说一声。

 

再来看看项目的设计,整个项目功能很简单,都是基于以上五个功能来实现的,所以我定出了这几个功能的接口,然后所有功能组件都基于这个接口可以灵活拓展不同的处理组件,下面的代码片段是功能接口的定义。

    public interface IPdfComponentFunc
    {
        string ComponentName { get; }
        void ToJpeg(string absoluteFilePath, string outputPath);
        void ToTxt(string absoluteFilePath, string outputPath);
        void FromXps(string absoluteFilePath, string outputPath);
        void InsertPage(string absoluteFilePath, string outputPath);
        void AddWaterprint(string absoluteFilePath, string outputPath);
    }

 

功能相信看名字就知道了,然后接下来是三个不同组件的代码实现。

Aspose.Pdf(由于试用版只能转4页,一怒之下搞了个破解版)

    public class AsposePdfComponent : IPdfComponentFunc
    {
        public string ComponentName => "Aspose.Pdf";

        private const string Key = "PExpY2Vuc2U+DQogIDxEYXRhPg0KICAgIDxMaWNlbnNlZFRvPlNoYW5naGFpIEh1ZHVuIEluZm9ybWF0aW9uIFRlY2hub2xvZ3kgQ28uLCBMdGQ8L0xpY2Vuc2VkVG8+DQogICAgPEVtYWlsVG8+MzE3NzAxODA5QHFxLmNvbTwvRW1haWxUbz4NCiAgICA8TGljZW5zZVR5cGU+RGV2ZWxvcGVyIE9FTTwvTGljZW5zZVR5cGU+DQogICAgPExpY2Vuc2VOb3RlPkxpbWl0ZWQgdG8gMSBkZXZlbG9wZXIsIHVubGltaXRlZCBwaHlzaWNhbCBsb2NhdGlvbnM8L0xpY2Vuc2VOb3RlPg0KICAgIDxPcmRlcklEPjE2MDkwMjAwNDQwMDwvT3JkZXJJRD4NCiAgICA8VXNlcklEPjI2NjE2NjwvVXNlcklEPg0KICAgIDxPRU0+VGhpcyBpcyBhIHJlZGlzdHJpYnV0YWJsZSBsaWNlbnNlPC9PRU0+DQogICAgPFByb2R1Y3RzPg0KICAgICAgPFByb2R1Y3Q+QXNwb3NlLlRvdGFsIGZvciAuTkVUPC9Qcm9kdWN0Pg0KICAgIDwvUHJvZHVjdHM+DQogICAgPEVkaXRpb25UeXBlPkVudGVycHJpc2U8L0VkaXRpb25UeXBlPg0KICAgIDxTZXJpYWxOdW1iZXI+NzM4MDNhYmUtYzZkMi00MTY3LTg2MTgtN2I0NDViNDRmOGY0PC9TZXJpYWxOdW1iZXI+DQogICAgPFN1YnNjcmlwdGlvbkV4cGlyeT4yMDE3MDkwNzwvU3Vic2NyaXB0aW9uRXhwaXJ5Pg0KICAgIDxMaWNlbnNlVmVyc2lvbj4zLjA8L0xpY2Vuc2VWZXJzaW9uPg0KICAgIDxMaWNlbnNlSW5zdHJ1Y3Rpb25zPmh0dHA6Ly93d3cuYXNwb3NlLmNvbS9jb3Jwb3JhdGUvcHVyY2hhc2UvbGljZW5zZS1pbnN0cnVjdGlvbnMuYXNweDwvTGljZW5zZUluc3RydWN0aW9ucz4NCiAgPC9EYXRhPg0KICA8U2lnbmF0dXJlPm5LNVVUR3dZMWVJSEtIV0d2NW5sQUxXUy81bDEzWkFuamlvdnlBcGNqQis0ZjNGbm5yOWhjeUlzazlvVzQySWp0ZFYra2JHZlNSMUV4OUozSGlkaThCeE43aHFiR1BERXNaWGo2RlYxaGl1N2MxWmUyNEp3VGc2UnpsNUNJRHY1YVhxbDQyczBkSGw4eXpreDRBM2RTTU5KTzRiQ094a2V2OFBiOWxSaUc3ST08L1NpZ25hdHVyZT4NCjwvTGljZW5zZT4=";
        private static Stream LStream = (Stream)new MemoryStream(Convert.FromBase64String(Key));
        public AsposePdfComponent()
        {
            SetPdfLicense();
        }

        public void SetPdfLicense()
        {
            var l = new Aspose.Pdf.License();
            //l.SetLicense(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Aspose.Total.lic"));
            l.SetLicense(LStream);
        }


        public void AddWaterprint(string absoluteFilePath, string outputPath)
        {
            var watermarkImgPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DefaultResource", "waterMarkImg.jpeg");
            using (var pdfDocument = new Document(absoluteFilePath))
            using (BackgroundArtifact background = new BackgroundArtifact())
            {
                foreach (Page aPdfPage in pdfDocument.Pages)
                {
                    ImageStamp imageStamp = new ImageStamp(watermarkImgPath);
                    imageStamp.XIndent = 300;
                    imageStamp.YIndent = aPdfPage.PageInfo.Height - 200;
                    imageStamp.Height = 81;
                    imageStamp.Width = 80;
                    aPdfPage.AddStamp(imageStamp);
                }


                pdfDocument.Save(outputPath);
            }
        }

        public void FromXps(string absoluteFilePath, string outputPath)
        {
            // Instantiate LoadOption object using XPS load option
            Aspose.Pdf.LoadOptions options = new XpsLoadOptions();

            // Create document object 
            Aspose.Pdf.Document document = new Aspose.Pdf.Document(absoluteFilePath, options);

            // Save the resultant PDF document
            document.Save(outputPath);
        }

        public void InsertPage(string absoluteFilePath, string outputPath)
        {
            var insertPageImgPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DefaultResource","insertPage.jpeg");
            using (var pdfDocument = new Document(absoluteFilePath))
            using (BackgroundArtifact background = new BackgroundArtifact())
            {
                // Add a new page to document object
                Page page = pdfDocument.Pages.Insert(2);
                page.AddImage(insertPageImgPath, page.Rect);

                pdfDocument.Save(outputPath);
            }
        }

        public void ToJpeg(string absoluteFilePath, string outputPath)
        {
            using (var pdfDocument = new Document(absoluteFilePath))
            {
                for(var i = 1; i < pdfDocument.Pages.Count + 1; i++)
                {

                    using (FileStream imageStream = new FileStream(Path.Combine(outputPath, i.ToString() + ".jpeg"), FileMode.Create))
                    {
                        //Quality [0-100], 100 is Maximum
                        //create Resolution object
                        Resolution resolution = new Resolution(300);
                        JpegDevice jpegDevice = new JpegDevice(resolution, 100);

                        //convert a particular page and save the image to stream
                        jpegDevice.Process(pdfDocument.Pages[i], imageStream);

                        //close stream
                        imageStream.Close();
                    }
                }
            }
        }

        public void ToTxt(string absoluteFilePath, string outputPath)
        {
            var txtAbsorber = new TextAbsorber();
            using (var pdfDocument = new Document(absoluteFilePath))
            {
                pdfDocument.Pages.Accept(txtAbsorber);
                File.WriteAllText(outputPath, txtAbsorber.Text);
            }
        }
    }

 

不想代码篇幅太长,所以另外两个组件的代码实现不贴了。

当你每实现一个接口,程序都会自动把它添加到组件(UseComponent)的下拉列表中,如下图所示。

 

 

 

因为考虑现在开源生态国际化,所以都写的英文,本人比较懒,也就没做多语言。

Demo使用方法Github上都有写明,这里只贴一下运行后的界面图

使用组件:Aspose,执行次数:10,耗时:27473 毫秒

 

 

 

 结尾语


 

简单地说下我这边的一个预研结果,我这里测试了一堆以前有问题的Pdf,最终发现:

Aspose.Pdf:1,从Xps转到Pdf的功能存在某些缺陷问题,有些Xps文档直接抛了异常。2,转Pdf到Image的功能对系统字体库有依赖,如果缺少Pdf文件的字体,转出来都是乱码。已经联系他们的技术团队看是否能改善 这两个问题,我就会选用它,目前在我这边印象最好。

Spire.Pdf:1,Pdf转Image的速度要比Aspose快,不过遗憾的是,带有图片的Pdf转Image直接抛了异常。2,转Txt的格式比起Aspose要差很多,但内容没什么毛病。

iText7:1,跟Spire一样,带图片的Pdf处理起来直接抛异常,而且iText没有Xps转Pdf和转Jpeg功能(我翻遍了文档没发现)

 

值得一提的是,速度最快的是iText7,虽然它功能不全,其次是Spire,不过Aspose转出来的东西很少会出问题,Txt的格式基本还原Pdf的原格式,反观Spire和iText这点就很不好,全部密密麻麻排一块去了。

 

这里只实现了我觉得比较好的三个组件来对比,如果有同学有其他更好的组件麻烦推荐下,我可以添加到组件中去,你也可以直接到Github上去Fork这份代码然后拓展自己的组件进行研究。

最近把自己网站重构成了.net core2.0,真的感觉到微软的良苦用心,微软都这么推行开源了,作为从c#出身,现在却在带Web组的人只希望C#系的开源生态能越来越好,不然我就要投奔NodeJs了(笑)。

转载于:https://www.cnblogs.com/RobotZero/p/7742282.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30587927/article/details/98262998

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签