web-dev-qa-db-ja.com

iTextSharpを使用してHTMLをPDFに変換する方法

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>
62
Chris Haas

まず、HTMLとPDFは、ほぼ同時期に作成されたものの、関連していません。 HTMLは、段落や表などの高レベルの情報を伝えることを目的としています。それを制御する方法はありますが、これらのより高いレベルの概念を描くのは最終的にブラウザ次第です。 PDFは、ドキュメントおよびドキュメントmustを「見る」ことを目的としていますレンダリングされる場所はどこでも同じです。

HTMLドキュメントには、100%幅の段落があり、モニターの幅に応じて2行または10行かかる場合があり、印刷する場合は7行になることがあり、携帯電話で見ると20行かかります。ただし、PDFファイルは、レンダリングデバイスに依存しないである必要があるため、画面サイズに関係なく常にレンダリングする必要があります。

上記のmustsのため、PDFは「テーブル」や「段落」などの抽象的なものをサポートしていません。 PDFがサポートする3つの基本的なものがあります:テキスト、線/形状、画像。 (注釈や映画のような他のものがありますが、ここではシンプルにしようとしています。)PDFでは、「here's a段落、ブラウザはあなたのことをする!」。代わりに、「このテキストをこの正確なフォントを使用してこの正確なX、Yの位置に描画します。心配する必要はありません。テキストの幅を以前に計算したので、すべてこの行に収まります」また、「ここにテーブルがある」とは言わず、代わりに「このテキストをこの正確な位置に描いてから、以前に計算した他の正確な位置に長方形を描くので、テキストの周りにあるように見える」 「。

第二に、iTextとiTextSharpはHTMLとCSSを解析します。それでおしまい。 ASP.Net、MVC、Razor、Struts、SpringなどはすべてHTMLフレームワークですが、iText/iTextSharpはそれらを100%認識していません。すべてフレームワーク固有の抽象化であるDataGridViews、Repeater、Templates、Viewsなどと同じです。選択したフレームワークからHTMLを取得するのはyourの責任であり、iTextは役に立ちません。 The document has no pagesという例外が発生した場合、または「iTextがHTMLを解析していない」と思われる場合は、 実際にはしないHTMLがある であることがほぼ確実です。あなたがやる。

第三に、長年使用されてきた組み込みクラスはHTMLWorkerですが、これはXMLWorkerJava / 。Net )に置き換えられています。 CSSファイルをサポートせず、最も基本的なCSSプロパティと実際には 特定のタグでのブレーク のみをサポートするHTMLWorkerでゼロ作業が行われています。 このファイルにHTML属性またはCSSプロパティと値 が表示されない場合は、おそらくHTMLWorkerでサポートされていません。 XMLWorkerはより複雑になることがありますが、それらの複雑さも make itmoreextensible です。

以下は、作業中のドキュメントに自動的に追加されるiText抽象化にHTMLタグを解析する方法を示すC#コードです。 C#とJavaは非常に似ているため、これを変換するのは比較的簡単です。例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への要求には朗報があります。 この回答が示したW3C標準 css-break- が問題を解決します。 ..これは、テスト後、今年の最終的な勧告に変わる計画を持つ候補勧告です。

print-css.rocks で示されているように、C#用のプラグインを使用したソリューションはそれほど標準的ではありません。

138
Chris Haas

@Chris HaasはitextSharpを使用してHTMLPDFに変換する方法を非常によく説明してくれました。
私の追加:
HtmlTextWriterを使用することで、HTMLテーブル+インラインCSS内にhtmlタグを配置し、XMLWorkerを使用せずにPDFを取得しました。
編集:サンプルコードの追加:
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 Refrencesを含めます

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

お役に立てれば!
ありがとうございました

9
Sam

2018年の時点では、iText7(古いiTextSharpライブラリの次の反復)およびそのHTML to PDFパッケージも利用可能です:itext7.pdfhtml

使い方は簡単です:

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

メソッドにはさらに多くのオーバーロードがあります。

更新:iText *ファミリー製品には デュアルライセンスモデル :オープンソース向け無料、商用利用向け有料。

6

次のコードを使用して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();
                }
            }
        }
0
Jonas T