۱۳۹۰/۱۱/۱۹

iTextSharp و نمايش صحيح تاريخ در متني راست به چپ


خروجي PDF زير را در نظر بگيريد:

مشكلي را در آن مشاهده مي‌كنيد؟ اصل آن يا صحيح آن بايد به شكل زير باشد:


و اين وارونه نمايش دادن‌ها، دقيقا مشكلي است كه حين كار با iTextSharp براي نمايش متني مثلا به همراه يك تاريخ شمسي وجود دارد. البته اين مشكل هم اساسا به خود استاندارد يونيكد برمي‌گردد كه يك سري كاراكتر را «كاراكتر ضعيف» معرفي كرده؛ براي مثال كاراكتر اسلش بكار رفته در يك تاريخ هم از اين دست است. بنابراين PDF توليدي توسط iTextSharp از ديد استاندارد يونيكد مشكلي ندارد، زيرا يك «نويسه ضعيف» مثل اسلش نمي‌تواند جهت را تغيير دهد؛ مگر اينكه از يك «نويسه قوي» براي دستكاري آن استفاده شود. براي مثال اين نويسه‌ها قوي هستند:

U+202A:   LEFT-TO-RIGHT EMBEDDING (LRE) 
U+202B:   RIGHT-TO-LEFT EMBEDDING (RLE) 
U+202D:   LEFT-TO-RIGHT OVERRIDE (LRO) 
U+202E:   RIGHT-TO-LEFT OVERRIDE (RLO) 
U+202C:   POP DIRECTIONAL FORMATTING (PDF) 

براي رسيدن به تصوير صحيح نمايش داده شده در بالا، متد FixWeakCharacters زير را تهيه كرده‌ام كه حداقل با iTextSharp جواب مي‌ده:

using System;
using System.Diagnostics;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace RleTests
{
    class Program
    {
        const char RightToLeftEmbedding = (char)0x202B;
        const char PopDirectionalFormatting = (char)0x202C;

        static string FixWeakCharacters(string data)
        {
            if (string.IsNullOrWhiteSpace(data)) return string.Empty;
            var weakCharacters = new[] { @"\", "/", "+", "-", "=", ";", "$" };
            foreach (var weakCharacter in weakCharacters)
            {
                data = data.Replace(weakCharacter, RightToLeftEmbedding + weakCharacter + PopDirectionalFormatting);
            }
            return data;
        }

        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\\Arial.ttf");
                Font tahoma = FontFactory.GetFont("Arial", BaseFont.IDENTITY_H);

                var table1 = new PdfPTable(1);
                table1.WidthPercentage = 100;

                var pdfCell = new PdfPCell
                {
                    RunDirection = PdfWriter.RUN_DIRECTION_RTL,
                    Border = 0,
                    Phrase = new Phrase(FixWeakCharacters(
                        "تاريخ: " + "1390/11/18" + Environment.NewLine +
                        "شماره پروژه: " + "1/2/3/4/56" + Environment.NewLine +
                        "اسلش: " + " 12/A/13 " + Environment.NewLine +
                        "بك اسلش: " + "  12\\13\\14 " + Environment.NewLine +
                        "مساوي و جمع: " + " 2+3=5 " + Environment.NewLine +
                        "سمي كولون: " + " 2=1+1; " + Environment.NewLine +
                        "دلار: " + "12$" + Environment.NewLine +
                        "كاما: " + "12,34,67" + Environment.NewLine +
                        "نقطه: " + "12.34" + Environment.NewLine +
                        "پرانتز: " + "متن (ساده)"
                        ),
                        tahoma)
                };

                table1.AddCell(pdfCell);
                pdfDoc.Add(table1);

            }

            Process.Start("Test.pdf");
        }
    }
}

از اين نوع مشكلات حين كار با HTML هم هست؛ وارونه نمايش داده شدن تاريخ فارسي در بين يك متن راست به چپ. البته در آنجا راه حل زير هم توصيه شده (بدون نياز به دستكاري نويسه‌ها):

<span dir="ltr" style="display:inline">1390/11/19</span>