【问题标题】:How to convert HTML to PDF using iTextSharp如何使用 iTextSharp 将 HTML 转换为 PDF
【发布时间】:2014-09-29 14:48:39
【问题描述】:

我想使用 iTextSharp 将以下 HTML 转换为 PDF,但不知道从哪里开始:

<style>
.headline{font-size:200%}
</style>
<p>
  This <em>is </em>
  <span class="headline" style="text-decoration: underline;">some</span>
  <strong>sample<em> text</em></strong>
  <span style="color: red;">!!!</span>
</p>

【问题讨论】:

    标签: c# pdf-generation itextsharp xmlworker


    【解决方案1】:

    首先,虽然 HTML 和 PDF 是在同一时间创建的,但它们并不相关。 HTML 旨在传达更高级别的信息,例如段落和表格。虽然有一些方法可以控制它,但最终要由浏览器来绘制这些更高级别的概念。 PDF 旨在传达文档,并且文档必须在任何呈现的地方“看起来”都一样。

    在 HTML 文档中,您可能有一个 100% 宽的段落,根据显示器的宽度,可能需要 2 行或 10 行,打印时可能需要 7 行,当您在电话可能需要20行。然而,PDF 文件必须独立于渲染设备,因此无论您的屏幕尺寸如何,它必须始终完全一样地渲染。

    由于上述必须,PDF 不支持“表格”或“段落”等抽象内容。 PDF 支持三种基本的东西:文本、线条/形状和图像。 (还有其他的东西,比如注释和电影,但我在这里尽量保持简单。) 在 PDF 中,你不会说“这是一段,浏览器做你的事!”。相反,您会说,“使用这个确切的字体在这个确切的 X、Y 位置绘制这个文本,不用担心,我之前已经计算了文本的宽度,所以我知道它都适合这条线”。您也没有说“这是一张桌子”,而是说“在这个确切位置绘制此文本,然后在我之前计算的另一个确切位置绘制一个矩形,所以我知道它会出现在文本周围”。

    其次,iText 和 iTextSharp 解析 HTML 和 CSS。而已。 ASP.Net、MVC、Razor、Struts、Spring 等都是 HTML 框架,但 iText/iTextSharp 100% 不知道它们。与 DataGridViews、Repeater、模板、视图等相同,它们都是特定于框架的抽象。从您选择的框架中获取 HTML 是 您的 责任,iText 不会帮助您。如果您收到 The document has no pages 的异常信息,或者您认为“iText 没有解析我的 HTML”,那么您几乎可以确定您是 don't actually have HTML,您只是认为自己在解析。

    第三,已经存在多年的内置类是HTMLWorker,但它已被XMLWorkerJava/.Net)取代。 HTMLWorker 正在完成零工作,它不支持 CSS 文件并且仅对最基本的 CSS 属性和实际上 breaks on certain tags 提供有限支持。如果您没有看到HTML attribute or CSS property and value in this file,那么HTMLWorker 可能不支持它。 XMLWorker 有时可能会更复杂,但这些并发症也会make it more extensible

    下面是 C# 代码,展示了如何将 HTML 标记解析为 iText 抽象,这些抽象会自动添加到您正在处理的文档中。 C# 和 Java 非常相似,因此转换它应该相对容易。 Example #1 使用内置的 HTMLWorker 来解析 HTML 字符串。由于仅支持内联样式,class="headline" 会被忽略,但其他所有内容都应该可以正常工作。示例 #2 与第一个示例相同,只是它使用 XMLWorker 代替。示例 #3 还解析了简单的 CSS 示例。

    //Create a byte array that will eventually hold our final PDF
    Byte[] bytes;
    
    //Boilerplate iTextSharp setup here
    //Create a stream that we can write to, in this case a MemoryStream
    using (var ms = new MemoryStream()) {
    
        //Create an iTextSharp Document which is an abstraction of a PDF but **NOT** a PDF
        using (var doc = new Document()) {
    
            //Create a writer that's bound to our PDF abstraction and our stream
            using (var writer = PdfWriter.GetInstance(doc, ms)) {
    
                //Open the document for writing
                doc.Open();
    
                //Our sample HTML and CSS
                var example_html = @"<p>This <em>is </em><span class=""headline"" style=""text-decoration: underline;"">some</span> <strong>sample <em> text</em></strong><span style=""color: red;"">!!!</span></p>";
                var example_css = @".headline{font-size:200%}";
    
                /**************************************************
                 * Example #1                                     *
                 *                                                *
                 * Use the built-in HTMLWorker to parse the HTML. *
                 * Only inline CSS is supported.                  *
                 * ************************************************/
    
                //Create a new HTMLWorker bound to our document
                using (var htmlWorker = new iTextSharp.text.html.simpleparser.HTMLWorker(doc)) {
    
                    //HTMLWorker doesn't read a string directly but instead needs a TextReader (which StringReader subclasses)
                    using (var sr = new StringReader(example_html)) {
    
                        //Parse the HTML
                        htmlWorker.Parse(sr);
                    }
                }
    
                /**************************************************
                 * Example #2                                     *
                 *                                                *
                 * Use the XMLWorker to parse the HTML.           *
                 * Only inline CSS and absolutely linked          *
                 * CSS is supported                               *
                 * ************************************************/
    
                //XMLWorker also reads from a TextReader and not directly from a string
                using (var srHtml = new StringReader(example_html)) {
    
                    //Parse the HTML
                    iTextSharp.tool.xml.XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, srHtml);
                }
    
                /**************************************************
                 * Example #3                                     *
                 *                                                *
                 * Use the XMLWorker to parse HTML and CSS        *
                 * ************************************************/
    
                //In order to read CSS as a string we need to switch to a different constructor
                //that takes Streams instead of TextReaders.
                //Below we convert the strings into UTF8 byte array and wrap those in MemoryStreams
                using (var msCss = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(example_css))) {
                    using (var msHtml = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(example_html))) {
    
                        //Parse the HTML
                        iTextSharp.tool.xml.XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, msHtml, msCss);
                    }
                }
    
    
                doc.Close();
            }
        }
    
        //After all of the PDF "stuff" above is done and closed but **before** we
        //close the MemoryStream, grab all of the active bytes from the stream
        bytes = ms.ToArray();
    }
    
    //Now we just need to do something with those bytes.
    //Here I'm writing them to disk but if you were in ASP.Net you might Response.BinaryWrite() them.
    //You could also write the bytes to a database in a varbinary() column (but please don't) or you
    //could pass them to another function for further PDF processing.
    var testFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.pdf");
    System.IO.File.WriteAllBytes(testFile, bytes);
    

    2017 年更新

    对于 HTML 到 PDF 的需求有好消息。作为this answer showedW3C 标准css-break-3 将解决这个问题...这是一个候选推荐,计划在今年经过测试后变成最终推荐。

    正如print-css.rocks 所示,有一些解决方案不那么标准,带有 C# 插件。

    【讨论】:

    • 很好的例子。谢谢。
    • 该代码声明了一个“new Document()”,并确定此 Document 类型是一个“iTextSharp Document”。此引用应完全命名为“iTextSharp.text.Document()”。我使用 iTextSharp 的项目已经有一个 Document 类,我不得不深入研究 iTextSharp 命名空间来更正引用。
    • 带有“iTextSharp.text.html.simpleparser.HTMLWorker(doc))”的行表示“htmlWorker”在 5.5.10 中已过时。这应该改成什么?
    • iTextSharp.tool namespace 给我一个不存在的错误,我也得到iTextSharp.text.html.simpleparser.HTMLWorker(doc)) is obsolete Version (5.5.8.0)
    • 如果有人正在寻找 iTextSharp.tool 的解决方案,您必须执行 NuGet 命令:Install-Package itextsharp.xmlworker
    【解决方案2】:

    截至 2018 年,还有 iText7(旧 iTextSharp 库的下一次迭代)及其 HTML 到 PDF 包可用:itext7.pdfhtml

    用法很简单:

    HtmlConverter.ConvertToPdf(
        new FileInfo(@"Path\to\Html\File.html"),
        new FileInfo(@"Path\to\Pdf\File.pdf")
    );
    

    方法有更多的重载。

    更新: iText* 系列产品有dual licensing model:开源免费,商业用途付费。

    【讨论】:

    • @rst 已更新。其他答案也提到了 iText 库(没有注意到它的商业性质),——你可能也想挑剔它们......
    • 我不挑剔。
    • CSS page-break-after 有效!
    【解决方案3】:

    @Chris Haas 已经很好地解释了如何使用itextSharpHTML 转换为PDF,非常有帮助
    我的补充是:
    通过使用HtmlTextWriter,我将html标签放在HTML表格+内联CSS中,我得到了我想要的PDF,而不使用XMLWorker
    编辑:添加示例代码:
    ASPX 页面:

    <asp:Panel runat="server" ID="PendingOrdersPanel">
     <!-- to be shown on PDF-->
     <table style="border-spacing: 0;border-collapse: collapse;width:100%;display:none;" >
     <tr><td><img src="abc.com/webimages/logo1.png" style="display: none;" width="230" /></td></tr>
    <tr style="line-height:10px;height:10px;"><td style="display:none;font-size:9px;color:#10466E;padding:0px;text-align:right;">blablabla.</td></tr>
     <tr style="line-height:10px;height:10px;"><td style="display:none;font-size:9px;color:#10466E;padding:0px;text-align:right;">blablabla.</td></tr>
     <tr style="line-height:10px;height:10px;"><td style="display:none;font-size:9px;color:#10466E;padding:0px;text-align:right;">blablabla</td></tr>
    <tr style="line-height:10px;height:10px;"><td style="display:none;font-size:9px;color:#10466E;padding:0px;text-align:right;">blablabla</td></tr>
    <tr style="line-height:10px;height:10px;"><td style="display:none;font-size:11px;color:#10466E;padding:0px;text-align:center;"><i>blablabla</i> Pending orders report<br /></td></tr>
     </table>
    <asp:GridView runat="server" ID="PendingOrdersGV" RowStyle-Wrap="false" AllowPaging="true" PageSize="10" Width="100%" CssClass="Grid" AlternatingRowStyle-CssClass="alt" AutoGenerateColumns="false"
       PagerStyle-CssClass="pgr" HeaderStyle-ForeColor="White" PagerStyle-HorizontalAlign="Center" HeaderStyle-HorizontalAlign="Center" RowStyle-HorizontalAlign="Center" DataKeyNames="Document#" 
          OnPageIndexChanging="PendingOrdersGV_PageIndexChanging" OnRowDataBound="PendingOrdersGV_RowDataBound" OnRowCommand="PendingOrdersGV_RowCommand">
       <EmptyDataTemplate><div style="text-align:center;">no records found</div></EmptyDataTemplate>
        <Columns>                                           
         <asp:ButtonField CommandName="PendingOrders_Details" DataTextField="Document#" HeaderText="Document #" SortExpression="Document#" ItemStyle-ForeColor="Black" ItemStyle-Font-Underline="true"/>
          <asp:BoundField DataField="Order#" HeaderText="order #" SortExpression="Order#"/>
         <asp:BoundField DataField="Order Date" HeaderText="Order Date" SortExpression="Order Date" DataFormatString="{0:d}"></asp:BoundField> 
        <asp:BoundField DataField="Status" HeaderText="Status" SortExpression="Status"></asp:BoundField>
        <asp:BoundField DataField="Amount" HeaderText="Amount" SortExpression="Amount" DataFormatString="{0:C2}"></asp:BoundField> 
       </Columns>
        </asp:GridView>
    </asp:Panel>
    

    C#代码:

    protected void PendingOrdersPDF_Click(object sender, EventArgs e)
    {
        if (PendingOrdersGV.Rows.Count > 0)
        {
            //to allow paging=false & change style.
            PendingOrdersGV.HeaderStyle.ForeColor = System.Drawing.Color.Black;
            PendingOrdersGV.BorderColor = Color.Gray;
            PendingOrdersGV.Font.Name = "Tahoma";
            PendingOrdersGV.DataSource = clsBP.get_PendingOrders(lbl_BP_Id.Text);
            PendingOrdersGV.AllowPaging = false;
            PendingOrdersGV.Columns[0].Visible = false; //export won't work if there's a link in the gridview
            PendingOrdersGV.DataBind();
    
            //to PDF code --Sam
            string attachment = "attachment; filename=report.pdf";
            Response.ClearContent();
            Response.AddHeader("content-disposition", attachment);
            Response.ContentType = "application/pdf";
            StringWriter stw = new StringWriter();
            HtmlTextWriter htextw = new HtmlTextWriter(stw);
            htextw.AddStyleAttribute("font-size", "8pt");
            htextw.AddStyleAttribute("color", "Grey");
    
            PendingOrdersPanel.RenderControl(htextw); //Name of the Panel
            Document document = new Document();
            document = new Document(PageSize.A4, 5, 5, 15, 5);
            FontFactory.GetFont("Tahoma", 50, iTextSharp.text.BaseColor.BLUE);
            PdfWriter.GetInstance(document, Response.OutputStream);
            document.Open();
    
            StringReader str = new StringReader(stw.ToString());
            HTMLWorker htmlworker = new HTMLWorker(document);
            htmlworker.Parse(str);
    
            document.Close();
            Response.Write(document);
        }
    }
    

    当然包括对 cs 文件的 iTextSharp 引用

    using iTextSharp.text;
    using iTextSharp.text.pdf;
    using iTextSharp.text.html.simpleparser;
    using iTextSharp.tool.xml;
    

    希望这会有所帮助!
    谢谢

    【讨论】:

    • 这段代码可以正常工作。但是下载后我无法查看pdf文件。我做错了什么?
    • 如果此代码适合您,您应该能够在下载后看到您的 PDF。我建议您在代码中发布一个问题以查看错误在哪里。还可以尝试从不同的浏览器运行您的代码,看看您是否能够查看 PDF。
    【解决方案4】:

    我使用以下代码创建 PDF

    protected void CreatePDF(Stream stream)
            {
                using (var document = new Document(PageSize.A4, 40, 40, 40, 30))
                {
                    var writer = PdfWriter.GetInstance(document, stream);
                    writer.PageEvent = new ITextEvents();
                    document.Open();
    
                    // instantiate custom tag processor and add to `HtmlPipelineContext`.
                    var tagProcessorFactory = Tags.GetHtmlTagProcessorFactory();
                    tagProcessorFactory.AddProcessor(
                        new TableProcessor(),
                        new string[] { HTML.Tag.TABLE }
                    );
    
                    //Register Fonts.
                    XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
                    fontProvider.Register(HttpContext.Current.Server.MapPath("~/Content/Fonts/GothamRounded-Medium.ttf"), "Gotham Rounded Medium");
                    CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
    
                    var htmlPipelineContext = new HtmlPipelineContext(cssAppliers);
                    htmlPipelineContext.SetTagFactory(tagProcessorFactory);
    
                    var pdfWriterPipeline = new PdfWriterPipeline(document, writer);
                    var htmlPipeline = new HtmlPipeline(htmlPipelineContext, pdfWriterPipeline);
    
                    // get an ICssResolver and add the custom CSS
                    var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(true);
                    cssResolver.AddCss(CSSSource, "utf-8", true);
                    var cssResolverPipeline = new CssResolverPipeline(
                        cssResolver, htmlPipeline
                    );
    
                    var worker = new XMLWorker(cssResolverPipeline, true);
                    var parser = new XMLParser(worker);
                    using (var stringReader = new StringReader(HTMLSource))
                    {
                        parser.Parse(stringReader);
                        document.Close();
                        HttpContext.Current.Response.ContentType = "application /pdf";
                        if (base.View)
                            HttpContext.Current.Response.AddHeader("content-disposition", "inline;filename=\"" + OutputFileName + ".pdf\"");
                        else
                            HttpContext.Current.Response.AddHeader("content-disposition", "attachment;filename=\"" + OutputFileName + ".pdf\"");
                        HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
                        HttpContext.Current.Response.WriteFile(OutputPath);
                        HttpContext.Current.Response.End();
                    }
                }
            }
    

    【讨论】:

      【解决方案5】:

      这是我用作指南的链接。希望这会有所帮助!

      Converting HTML to PDF using ITextSharp

      protected void Page_Load(object sender, EventArgs e)
          {
              try
              {
                  string strHtml = string.Empty;
                  //HTML File path -http://aspnettutorialonline.blogspot.com/
                  string htmlFileName = Server.MapPath("~") + "\\files\\" + "ConvertHTMLToPDF.htm";
                  //pdf file path. -http://aspnettutorialonline.blogspot.com/
                  string pdfFileName = Request.PhysicalApplicationPath + "\\files\\" + "ConvertHTMLToPDF.pdf";
      
                  //reading html code from html file
                  FileStream fsHTMLDocument = new FileStream(htmlFileName, FileMode.Open, FileAccess.Read);
                  StreamReader srHTMLDocument = new StreamReader(fsHTMLDocument);
                  strHtml = srHTMLDocument.ReadToEnd();
                  srHTMLDocument.Close();
      
                  strHtml = strHtml.Replace("\r\n", "");
                  strHtml = strHtml.Replace("\0", "");
      
                  CreatePDFFromHTMLFile(strHtml, pdfFileName);
      
                  Response.Write("pdf creation successfully with password -http://aspnettutorialonline.blogspot.com/");
              }
              catch (Exception ex)
              {
                  Response.Write(ex.Message);
              }
          }
          public void CreatePDFFromHTMLFile(string HtmlStream, string FileName)
          {
              try
              {
                  object TargetFile = FileName;
                  string ModifiedFileName = string.Empty;
                  string FinalFileName = string.Empty;
      
                  /* To add a Password to PDF -http://aspnettutorialonline.blogspot.com/ */
                  TestPDF.HtmlToPdfBuilder builder = new TestPDF.HtmlToPdfBuilder(iTextSharp.text.PageSize.A4);
                  TestPDF.HtmlPdfPage first = builder.AddPage();
                  first.AppendHtml(HtmlStream);
                  byte[] file = builder.RenderPdf();
                  File.WriteAllBytes(TargetFile.ToString(), file);
      
                  iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(TargetFile.ToString());
                  ModifiedFileName = TargetFile.ToString();
                  ModifiedFileName = ModifiedFileName.Insert(ModifiedFileName.Length - 4, "1");
      
                  string password = "password";
                  iTextSharp.text.pdf.PdfEncryptor.Encrypt(reader, new FileStream(ModifiedFileName, FileMode.Append), iTextSharp.text.pdf.PdfWriter.STRENGTH128BITS, password, "", iTextSharp.text.pdf.PdfWriter.AllowPrinting);
                  //http://aspnettutorialonline.blogspot.com/
                  reader.Close();
                  if (File.Exists(TargetFile.ToString()))
                      File.Delete(TargetFile.ToString());
                  FinalFileName = ModifiedFileName.Remove(ModifiedFileName.Length - 5, 1);
                  File.Copy(ModifiedFileName, FinalFileName);
                  if (File.Exists(ModifiedFileName))
                      File.Delete(ModifiedFileName);
      
              }
              catch (Exception ex)
              {
                  throw ex;
              }
          }
      

      您可以下载示例文件。只需将要转换的html 放入files 文件夹并运行即可。它将自动生成pdf文件并将其放在同一文件夹中。但在您的情况下,您可以在 htmlFileName 变量中指定您的 html 路径。

      【讨论】:

      • 啊,好的。看起来这里使用了一些 TestPDF.HtmlToPdfBuilder 实用程序类来进行实际转换。 [...] 我刚刚下载了它。事实证明,它本质上是 iTextSharp HTMLWorker 类的包装,同时已被弃用/过时。
      • CreatePDFFromHTMLFile() 方法中的 TestPDF 是什么
      猜你喜欢
      • 2022-03-07
      • 1970-01-01
      • 2014-12-05
      • 1970-01-01
      • 1970-01-01
      • 2016-07-08
      • 2011-05-22
      相关资源
      最近更新 更多