۱۳۹۰/۰۶/۱۲

تبديل HTML به PDF با استفاده از كتابخانه‌ي iTextSharp


روش متداول كار با كتابخانه‌ي iTextSharp ، ايجاد شيء Document ، سپس ايجاد PdfWriter براي نوشتن در آن، گشودن سند و ... افزودن اشيايي مانند Paragraph ، PdfPTable ، PdfPCell و غيره به آن است و در نهايت بستن سند. راه ميانبري هم براي كار با اين كتابخانه وجود دارد و آن هم استفاده از امكانات فضاي نام iTextSharp.text.html.simpleparser آن مي‌باشد. به اين ترتيب مي‌توان به صورت خودكار، يك محتواي HTML را تبديل به فايل PDF كرد.

مثال : نمايش يك متن HTML ساده انگليسي
using System.Diagnostics;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.html.simpleparser;
using iTextSharp.text.pdf;

namespace HeadersAndFooters
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var pdfDoc = new Document(PageSize.A4))
            {
                PdfWriter.GetInstance(pdfDoc, new FileStream("Test.pdf", FileMode.Create));
                pdfDoc.Open();

                var html = @"<span style='color:blue'><b>Testing</b></span>
                             <i>iTextSharp's</i> <u>HTML to PDF capabilities</u>";
                var parsedHtmlElements = HTMLWorker.ParseToList(new StringReader(html), null);

                foreach (var htmlElement in parsedHtmlElements)
                {
                    pdfDoc.Add(htmlElement);
                }
            }

            //open the final file with adobe reader for instance.
            Process.Start("Test.pdf");
        }
    }
}


نكته‌ي جديد كد فوق، استفاده از متد HTMLWorker.ParseToList است. به اين ترتيب parser كتابخانه‌ي iTextSharp وارد عمل شده و html تعريف شده را به معادل المان‌هاي بومي خودش تبديل مي‌كند؛ مثلا تبديل به chunk يا pdfptable و امثال آن. در نهايت در طي يك حلقه، اين عناصر به صفحه اضافه مي‌شوند.
البته بايد دقت داشت كه HTMLWorker امكان تبديل عناصر پيچيده، تودرتو و چندلايه HTML را ندارد؛ اما بهتر از هيچي است!

همه‌ي اين‌ها خوب! اما به درد ما فارسي زبان‌ها نمي‌خورد. همين متغير html فوق را با يك متن فارسي جايگزين كنيد، چيزي نمايش داده نخواهد شد. البته اين هم نكته دارد كه در ادامه ذكر خواهد شد.
جهت نمايش متون فارسي نياز است تا نكات ذكر شده در مطلب «فارسي نويسي و iTextSharp» رعايت شوند كه شامل:
- تعيين صريح قلم
- تعيين encoding
- استفاده از عناصر دربرگيرنده‌اي است كه خاصيت RunDirection را پشتيباني مي‌كنند؛ مانند PdfPCell و غيره


به اين ترتيب خواهيم داشت:
using System.Diagnostics;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.html.simpleparser;
using iTextSharp.text.pdf;
using iTextSharp.text.html;

namespace HeadersAndFooters
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var pdfDoc = new Document(PageSize.A4))
            {
                PdfWriter.GetInstance(pdfDoc, new FileStream("Test.pdf", FileMode.Create));
                pdfDoc.Open();                

                //روش صحيح تعريف فونت
                FontFactory.Register("c:\\windows\\fonts\\tahoma.ttf");

                StyleSheet styles = new StyleSheet();
                styles.LoadTagStyle(HtmlTags.BODY, HtmlTags.FONTFAMILY, "tahoma");
                styles.LoadTagStyle(HtmlTags.BODY, HtmlTags.ENCODING, "Identity-H");                

                var html = @"<span style='color:blue'><b>آزمايش</b></span>
                                كتابخانه <i>iTextSharp</i> <u>جهت بررسى فارسى نويسى</u>";
                var parsedHtmlElements = HTMLWorker.ParseToList(new StringReader(html), styles);

                PdfPCell pdfCell = new PdfPCell { Border = 0 };
                pdfCell.RunDirection = PdfWriter.RUN_DIRECTION_RTL;

                foreach (var htmlElement in parsedHtmlElements)
                {
                    pdfCell.AddElement(htmlElement);
                }

                var table1 = new PdfPTable(1);
                table1.AddCell(pdfCell);
                pdfDoc.Add(table1);
            }

            //open the final file with adobe reader for instance.
            Process.Start("Test.pdf");
        }
    }
}

همانطور كه ملاحظه مي‌كنيد ابتدا قلمي در cache قلم‌هاي اين كتابخانه ثبت مي‌شود (FontFactory.Register). سپس نوع قلم و encoding آن توسط يك StyleSheet تعريف شده و به HTMLWorker.ParseToList ارسال مي‌گردد و در نهايت به كمك يك المان داراي RunDirection، در صفحه نمايش داده مي‌شود.



نكته:
ممكن است كه به متغير html ، يك table ساده html را نسبت دهيد. در اين حالت پس از تنظيم style ياد شده، در هر سلول اين html table ، متون فارسي به صورت معكوس نمايش داده خواهند شد كه اين هم يك نكته‌ي كوچك ديگر دارد:

foreach (var htmlElement in parsedHtmlElements)
                {
                    if (htmlElement is PdfPTable)
                    {
                        var table = (PdfPTable)htmlElement;
                        table.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
                        foreach (var row in table.Rows)
                        {
                            foreach (var cell in row.GetCells())
                            {
                                cell.RunDirection = PdfWriter.RUN_DIRECTION_RTL;                               
                            }
                        }
                    }

                    pdfCell.AddElement(htmlElement);
                }

در قسمتي كه قرار است المان‌هاي معادل به pdfCell اضافه شوند، آن‌ها را بررسي كرده و RunDirection آن‌ها را RTL خواهيم كرد.


كاربردها:
بديهي است اين حالت براي تهيه گزارشات پيشرفته‌تر براي مثال تهيه قالب‌هايي كه در حين تهيه PDF ، قسمت‌هايي از آن‌ها توسط برنامه نويس Replace مي‌شوند، بسيار مناسب است.
همچنين مطلب «بارگذاري يك يوزركنترل با استفاده از جي‌كوئري» و متد RenderUserControl مطرح شده در آن كه در نهايت يك قطعه كد HTML را به صورت رشته به ما تحويل مي‌دهد، مي‌تواند جهت تهيه گزارش‌هاي پويايي كه براي مثال قسمتي از آن يك GridView بايند شده حاصل از يك يوزر كنترل است،‌ مورد استفاده قرار گيرد.