由於公司是作醫療相關軟件的,因此常常和文檔打交道,其中就包含了Pdf。醫院的Pdf(一般是他們的報告)都千奇百怪,而咱們一直以來都是在用一些免費且可能已經沒人維護了的組件來處理Pdf,因此就常常出現Pdf轉亂碼,甚至直接異常的狀況。跟公司管理層反應了好久,終於答應掏腰包採購一款Pdf的處理組件,而且交由我來預研。稍微調查了一下後,最終商業組件選中了 Aspose,Spire還有Leadtool這三家公司的產品,另外因爲iTextSharp做爲開源的Pdf處理組件太有名,因此我也把它的重寫版——iText7加入了對比的列表中,寫了一個能夠方便執行的Demo項目,想了想,爲了那些一樣須要Pdf相關資料的同窗,也爲了c# 系的開源生態,把這個項目特地整理了一下,發到了Github上,而且寫了一些簡單的使用說明。git
GitHub的地址請戳 這裏github
本項目主要以常見的五種Pdf處理來進行預研,包括「Pdf轉Jpeg」,「Pdf轉txt」,「Xps轉Pdf」,「將jpeg做爲插頁插入到Pdf中」,「加水印」。c#
在對上面提出的四種組件進行充分調查後,直接否認了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); }
功能相信看名字就知道了,而後接下來是三個不一樣組件的代碼實現。spa
Aspose.Pdf(因爲試用版只能轉4頁,一怒之下搞了個破解版).net
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)的下拉列表中,以下圖所示。3d
由於考慮如今開源生態國際化,因此都寫的英文,本人比較懶,也就沒作多語言。
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了(笑)。