• ASP.Net MVC——使用 ITextSharp 完美解决HTML转PDF(中文也可以)


    前言:

    最近在做老师交代的一个在线写实验报告的小项目中,有这么个需求:把学生提交的实验报告(HTML形式)直接转成PDF,方便下载和打印。

    以前都是直接用rdlc报表实现的,可这次牵扯到图片,并且更为重要的一点是 PDF的格式得跟学生提交的HMTL页面一样。经过网上查阅资料,

    找到了ITextSharp插件。

    ITextSharp很强大,但是在处理HMTL中的 img标签时,src中只能是绝对路径。 

    解决方法我写在了另一篇文章中

    正文:

    ITextSharp就不多介绍了。项目的链接下载链接为http://files.cnblogs.com/files/zuochengsi-9/H%E8%BD%ACPDF.zip

    下开始项目之前得添加 ITextSharp.dll和ITextSharp.xmlworker.dll  后者是解决中文用的

    可以从NuGet中下载引用,具体方法就不介绍了。网上很多解决方案。

    项目结构图:

    下面先说下主要操作:

    步骤:1、将本地的某个视图转成字符串。

             2、将字符串整合成PDF的文档,并返回byte数组。

             3、讲比特流写到HTTP内容主体的二进制流中去。

     视图转字符串代码:

    首先新建两个类,转字符串的逻辑主要在RenderViewToString方法中。

     public class HtmlViewRenderer
        {
            public string RenderViewToString(Controller controller, string viewName, object viewData)
            {
                var renderedView = new StringBuilder();
                using (var responseWriter = new StringWriter(renderedView))
                {
                    var fakeResponse = new HttpResponse(responseWriter);
                    var fakeContext = new HttpContext(HttpContext.Current.Request, fakeResponse);
                    var fakeControllerContext = new ControllerContext(new HttpContextWrapper(fakeContext), controller.ControllerContext.RouteData, controller.ControllerContext.Controller);
    
                    var oldContext = HttpContext.Current;
                    HttpContext.Current = fakeContext;
    
                    using (var viewPage = new ViewPage())
                    {
                        var html = new HtmlHelper(CreateViewContext(responseWriter, fakeControllerContext), viewPage);
                        html.RenderPartial(viewName,viewData);
                        HttpContext.Current = oldContext;
                    }
                }
    
                return renderedView.ToString();
            }
    
            private static ViewContext CreateViewContext(TextWriter responseWriter, ControllerContext fakeControllerContext)
            {
                return new ViewContext(fakeControllerContext, new FakeView(), new ViewDataDictionary(), new TempDataDictionary(), responseWriter);
            }
        }
       public class FakeView : IView
        {
    
            public void Render(ViewContext viewContext, TextWriter writer)
            {
                throw new NotImplementedException();
            }
    
        }

    再新建一个控制器,调用刚刚写好的RenderViewToString方法。(后面会再新建一个HomeController,继承这个PdfViewController,再在HomeController的Action里调用ViewPdf就行)

       public class PdfViewController : Controller
        {
            private readonly HtmlViewRenderer htmlViewRenderer;
    
            public PdfViewController()
            {
                this.htmlViewRenderer = new HtmlViewRenderer();
            }
    
            protected string ViewPdf(string viewName,object model)
            {
                // Render the view html to a string.
                string htmlText = this.htmlViewRenderer.RenderViewToString(this, viewName,model);
                return htmlText;
            }
    
        }

    "字符串转byte[]"   (这个方法放在后面写的HomeController中)

    public byte[] ConvertHtmlTextToPDF(string htmlText)
            {
                if (string.IsNullOrEmpty(htmlText))
                {
                    return null;
                }
                //避免當htmlText無任何html tag標籤的純文字時,轉PDF時會掛掉,所以一律加上<p>標籤
                htmlText = "<p>" + htmlText + "</p>";
    
                MemoryStream outputStream = new MemoryStream();//要把PDF寫到哪個串流
                byte[] data = Encoding.UTF8.GetBytes(htmlText);//字串轉成byte[]
                MemoryStream msInput = new MemoryStream(data);
                Document doc = new Document();//要寫PDF的文件,建構子沒填的話預設直式A4
                PdfWriter writer = PdfWriter.GetInstance(doc, outputStream);
                //指定文件預設開檔時的縮放為100%
                PdfDestination pdfDest = new PdfDestination(PdfDestination.XYZ, 0, doc.PageSize.Height, 1f);
                //開啟Document文件 
                doc.Open();
                //使用XMLWorkerHelper把Html parse到PDF檔裡
                XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, msInput, null, Encoding.UTF8, new UnicodeFontFactory());
                //將pdfDest設定的資料寫到PDF檔
                PdfAction action = PdfAction.GotoLocalPage(1, pdfDest, writer);
                writer.SetOpenAction(action);
                doc.Close();
                msInput.Close();
                outputStream.Close();
                //回傳PDF檔案 
                return outputStream.ToArray();
    
            }

    其中XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, msInput, null, Encoding.UTF8, new UnicodeFontFactory());这段代码中的“UnicodeFontFactory”类,封装了中文字体的设置。代码如下:

    public class UnicodeFontFactory : FontFactoryImp 
        {
            private static readonly string arialFontPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts),
                "arialuni.ttf");//arial unicode MS是完整的unicode字型。
            private static readonly string 標楷體Path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts),
              "KAIU.TTF");//標楷體
    
    
            public override Font GetFont(string fontname, string encoding, bool embedded, float size, int style, BaseColor color,
                bool cached)
            {
                //可用Arial或標楷體,自己選一個
                BaseFont baseFont = BaseFont.CreateFont(標楷體Path, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
                return new Font(baseFont, size, style, color);
            } 
        }

    再新建一个类,用来将比特流输出到response.OutputStream中:

     public class BinaryContentResult : ActionResult
        {
            private readonly string contentType;
            private readonly byte[] contentBytes;
    
            public BinaryContentResult(byte[] contentBytes, string contentType)
            {
                this.contentBytes = contentBytes;
                this.contentType = contentType;  
            }
    
            public override void ExecuteResult(ControllerContext context)
            {
                var response = context.HttpContext.Response;
                response.Clear();
                response.Cache.SetCacheability(HttpCacheability.Public);
                response.ContentType = this.contentType;
    //下面这段加上就是一个下载页面
    response.AppendHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode("文件名.pdf", System.Text.Encoding.UTF8));
    using (var stream = new MemoryStream(this.contentBytes)) { stream.WriteTo(response.OutputStream); stream.Flush(); } } }

    现在来看一下HomeController

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Text;
    using System.Web;
    using System.Web.Mvc;
    using HTML转PDF.Models;
    using iTextSharp.text;
    using iTextSharp.text.pdf;
    using iTextSharp.tool.xml;
    using ReportManagement;
    
    namespace HTML转PDF.Controllers
    {
        public class HomeController :PdfViewController
        {
            //
            // GET: /Home/
    
            public ActionResult Index()
            {
                return View();
            }
            /// <summary>
            /// 執行此Url,下載PDF檔案
            /// </summary>
            /// <returns></returns>
            public ActionResult DownloadPdf()
            {
                var person = new People("左成");
                string htmlText = this.ViewPdf("Preview",person);
                byte[] pdfFile = this.ConvertHtmlTextToPDF(htmlText);
                return new BinaryContentResult(pdfFile, "application/pdf");
            }
            /// <summary>
            /// 將Html文字 輸出到PDF檔裡
            /// </summary>
            /// <param name="htmlText"></param>
            /// <returns></returns>
            public byte[] ConvertHtmlTextToPDF(string htmlText)
            {
                if (string.IsNullOrEmpty(htmlText))
                {
                    return null;
                }
                //避免當htmlText無任何html tag標籤的純文字時,轉PDF時會掛掉,所以一律加上<p>標籤
                htmlText = "<p>" + htmlText + "</p>";
    
                MemoryStream outputStream = new MemoryStream();//要把PDF寫到哪個串流
                byte[] data = Encoding.UTF8.GetBytes(htmlText);//字串轉成byte[]
                MemoryStream msInput = new MemoryStream(data);
                Document doc = new Document();//要寫PDF的文件,建構子沒填的話預設直式A4
                PdfWriter writer = PdfWriter.GetInstance(doc, outputStream);
                //指定文件預設開檔時的縮放為100%
                PdfDestination pdfDest = new PdfDestination(PdfDestination.XYZ, 0, doc.PageSize.Height, 1f);
                //開啟Document文件 
                doc.Open();
                //使用XMLWorkerHelper把Html parse到PDF檔裡
                XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, msInput, null, Encoding.UTF8, new UnicodeFontFactory());
                //將pdfDest設定的資料寫到PDF檔
                PdfAction action = PdfAction.GotoLocalPage(1, pdfDest, writer);
                writer.SetOpenAction(action);
                doc.Close();
                msInput.Close();
                outputStream.Close();
                //回傳PDF檔案 
                return outputStream.ToArray();
    
            }
        }
    }

    步骤二的ConvertHtmlTextToPDF方法,我就直接放在了这里面,没管那些设计原则了。上面代码中“Preview”是本地的一个视图,最好和HomeController在一个区域里面,不然得改步骤一的代码。

     最后是我的前台部分的代码,写的很简单(Preview.cshtml)。

    @model HTML转PDF.Models.People
      <!-- 下面是我机器上的绝对路径  -->         
    <img src="E:12.bmp" width="64" height="64" /> 
    <p>大家好,我叫"@Model.Name"</p>
    

      

    特别注意:关于跳转到我们写的“DownloadPdf”Action时,千万不要用ajax.ActionLink(坑了我好久)。不然会出现乱码,我也没找到解决方案。

    参考资料:http://www.tuicool.com/articles/aUfqeu

           http://dwtedx.com/itshare_233.html

     转载请注明出处:http://www.cnblogs.com/zuochengsi-9/p/5483808.html

  • 相关阅读:
    C#关机代码实例详解
    如何设计通用的网站模板
    C# XML解析方式实例解析1
    ASP.NET配置错误页面浅析
    几种常用的C#排序方法简介
    简述C# XML解析方法的特点及应用
    请不要相信
    浅谈ASP.NET Forms验证
    设计友好的错误信息页面
    详解.NET中容易混淆的委托与接口
  • 原文地址:https://www.cnblogs.com/zuochengsi-9/p/5483808.html
Copyright © 2020-2023  润新知