我們的網站業務會生成一個報告,用網頁展示出來,要有生成pdf並下載的功能,關鍵是生成pdf。
用內容一段段去拼pdf,想想就很崩潰,所以就去網上找直接把html生成pdf的方法。
網上資料大部分都是用的iTextSharp的XMLWorkerHelper做的(代碼我貼在后面),遇到的問題是,它對css樣式的支持比較古老或者說簡單,所以重新改了一下我的html樣式,div大部分都換成了table等,搞定后運行了一段時間沒出什么問題。
但是,最近發現它有一種情況會報錯。我的html內容是一個訂單,包含多個小項,每個小項有自己的內容。最近發現如果小項的內容稍多,比如幾千個字符,生成pdf時在分頁那兒會陷入死循環。
由於我的小項一直被當成一整塊,如果一頁剩下的地方顯示不完,它會整個挪到下一頁,不會從中間截斷,所以我猜測,如果被它判定一段內容是不能分割的,而內容的長度就已經超出了一整頁的長度,就會出錯。
我試圖調整自己的內容的格式等等,讓它不被判定為不可分割,沒有成功,而且沒有看到源碼,最后沒有解決問題,只好找另外的方式生成pdf。
第二次映入眼簾的是wkhtmltopdf,同時有人提到了Pechkin,是作者在wkhtmltopdf基礎上開發的,更方便.NET開放使用,不過原版有個bug,有網友給出了修正版本。
這個的使用更簡單,不過比較奇葩的是,需要把幾個dll庫放到根目錄。
這一次接入完成后沒有出現內容長了就掛掉的情況,但是也有個毛病:
它的分頁非常“硬”,有可能會把一行字從中間攔腰截斷,分在上下兩頁。
兩害相權取其輕,只能先用着了。
如果能確定內容不會太長,還是iTextSharp比較好。
下面是兩種方式的使用代碼:
iTextSharp:
1 public class PDFHelper 2 { 3 public byte[] ConvertHtmlTextToPDF(string htmlText) 4 { 5 if (string.IsNullOrEmpty(htmlText)) 6 { 7 return null; 8 } 9 //避免當htmlText無任何html tag標簽的純文字時,轉PDF時會掛掉,所以一律加上<p>標簽 10 htmlText = "<p>" + htmlText + "</p>"; 11 MemoryStream outputStream = new MemoryStream();//要把PDF寫到哪個串流 12 byte[] data = Encoding.UTF8.GetBytes(htmlText);//字串轉成byte[] 13 MemoryStream msInput = new MemoryStream(data); 14 Document doc = new Document();//要寫PDF的文件,建構子沒填的話預設直式A4 15 16 PdfWriter writer = PdfWriter.GetInstance(doc, outputStream); 17 PdfDestination pdfDest = new PdfDestination(PdfDestination.XYZ, 0, doc.PageSize.Height, 1f); 18 //開啟Document文件 19 doc.Open(); 20 HeaderAndFooterEvent header = new HeaderAndFooterEvent(); 21 header.tpl = writer.DirectContent.CreateTemplate(100, 100); 22 writer.PageEvent = header; 23 24 //使用XMLWorkerHelper把Html parse到PDF檔里 25 XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, msInput, null, Encoding.UTF8, new UnicodeFontFactory()); 26 27 //將pdfDest設定的資料寫到PDF檔 28 PdfAction action = PdfAction.GotoLocalPage(1, pdfDest, writer); 29 writer.SetOpenAction(action); 30 doc.Close(); 31 msInput.Close(); 32 outputStream.Close(); 33 //回傳PDF檔案 34 return outputStream.ToArray(); 35 36 } 37 } 38 public class UnicodeFontFactory : FontFactoryImp 39 { 40 41 public override Font GetFont(string fontname, string encoding, bool embedded, float size, int style, BaseColor color, bool cached) 42 { 43 string FontPath = System.Web.Hosting.HostingEnvironment.MapPath("~/Font/"); 44 45 BaseFont bfYaHei = BaseFont.CreateFont(FontPath + "msyh.ttc,1", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); 46 return new Font(bfYaHei, size, style, color); 47 } 48 } 49 50 public class HeaderAndFooterEvent : PdfPageEventHelper, IPdfPageEvent 51 { 52 public PdfTemplate tpl = null; 53 public bool PAGE_NUMBER = true; 54 private int PageCount = 1; 55 private static string FontPath = System.Web.Hosting.HostingEnvironment.MapPath("~/Font/"); 56 private static BaseFont bfYaHei = BaseFont.CreateFont(FontPath + "msyh.ttc,1", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); 57 private static iTextSharp.text.Font font = new Font(bfYaHei, 10, Font.NORMAL, BaseColor.BLACK); 58 59 //重寫 關閉一個頁面時 60 public override void OnEndPage(PdfWriter writer, Document document) 61 { 62 if (PAGE_NUMBER) 63 { 64 Phrase footer = new Phrase("www.XXXX.com 第" + writer.PageNumber + "頁/共 頁", font); 65 PdfContentByte cb = writer.DirectContent; 66 67 //模版 顯示總共頁數 68 cb.AddTemplate(tpl, document.Right - 54 + document.LeftMargin, document.Bottom - 15);//調節模版顯示的位置 69 70 71 //頁腳顯示的位置 72 ColumnText.ShowTextAligned(cb, Element.ALIGN_CENTER, footer, document.Right - 297 + document.LeftMargin, document.Bottom - 14, 0); 73 } 74 } 75 //重寫 打開一個新頁面時 76 public override void OnStartPage(PdfWriter writer, Document document) 77 { 78 if (PAGE_NUMBER) 79 { 80 PageCount += 1; 81 writer.PageCount = PageCount; 82 } 83 } 84 //關閉PDF文檔時 85 public override void OnCloseDocument(PdfWriter writer, Document document) 86 { 87 tpl.BeginText(); 88 tpl.SetFontAndSize(bfYaHei, 10);//生成的模版的字體、顏色 89 tpl.ShowText(PageCount.ToString());//模版顯示的內容 90 tpl.EndText(); 91 tpl.ClosePath(); 92 } 93 //定義輸出文本 94 public static Paragraph InsertTitleContent(string text) 95 { 96 97 Paragraph paragraph = new Paragraph(text, font);//新建一行 98 99 paragraph.Alignment = Element.ALIGN_CENTER;//居中 100 101 paragraph.SpacingBefore = 5; 102 103 paragraph.SpacingAfter = 5; 104 paragraph.SetLeading(1, 2);//每行間的間隔 105 return paragraph; 106 } 107 }
Pechkin:
1 public static byte[] ConvertHtmlToPdf(string html) 2 { 3 4 try 5 { 6 using (IPechkin pechkin = Factory.Create(new GlobalConfig())) 7 { 8 ObjectConfig oc = new ObjectConfig(); 9 oc.SetPrintBackground(true) 10 .SetLoadImages(true).Footer.SetContentSpacing(30).SetLeftText("www.XXXX.com").SetRightText("[page]/[toPage]"); 11 byte[] pdf = pechkin.Convert(oc, html); 12 return pdf; 13 } 14 } 15 catch (Exception) 16 { 17 18 } 19 return null; 20 }
大家有更好的解決方式,希望賜教。
