Saturday, December 13, 2014

Winforms: Read and display Arabic characters from a text file

Recently I was working on a program which i need to read in and display a text file containing arabic characters. The problem gave me quite a bit of a challenge initially, therefore i decide to share the lesson that i learned while figuring out how to do that.

Read the text file containing Arabic characters

The essence lies on how you sent the encoding in your StreamReader object which reads the text file content. Firstly let me list out what will not work:

1. Default encoding like "StreamReader reader = new StreamReader(filepath)" will not work, you will get messed up text displayed when it comes to arabic characters.
2. Unicode encoding like "StreamReader reader = new StreamReader(filepath, System.Text.Encoding.Unicode)" will not work, your program will hang possibly.
3. UTF Encoding like "StreamReader reader = new StreamReader(filepath, System.Text.Encoding.UTF8)" will not work, you will get messed up arabic characters. Sames goes for UTF32, UTF7

Now below is what will work:

StreamReader reader = new StreamReader(filepath, System.Text.Encoding.GetEncoding("Arabic")

Display the text file containing arabic characters in a Winform control

Ok at this point, you may get it to work perfectly. but if you still cannot display text correctly, one solution is to apply a "Arial Unicode MS" font to your Winform controls. Below is the instruction on how to do this:

1. Download a copy of the "Arial Unicode MS" (google it), and embed into your program's resource file (copy and paste it directly in the "Resources" tab after your open your project's property page). Let's say the name of the embeded resource is "Arial_Unicode_MS".
2. Create a singleton class FontManager, which will be used to apply the "Arial_Unicode_MS" to the Winform. the codes of the FontManager is shown below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Text;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;

namespace Lab001
{
    public class FontManager
    {
        private static FontManager mInstance = null;
        private static object mSyncObj = new object();

        public static FontManager Instance
        {
            get
            {
                if (mInstance == null)
                {
                    lock (mSyncObj)
                    {
                        mInstance = new FontManager();
                    }
                }
                return mInstance;
            }
        }

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]

        private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont,
           IntPtr pdv, [System.Runtime.InteropServices.In] ref uint pcFonts);

        private PrivateFontCollection MYpfc = new PrivateFontCollection();

        private FontManager()
        {

            try
            {
                unsafe
                {
                    fixed (byte* pFontData = Properties.Resources.Arial_Unicode_MS)
                    {
                        uint dummy = 0;
                        MYpfc.AddMemoryFont((IntPtr)pFontData, Properties.Resources.Arial_Unicode_MS.Length);
                        AddFontMemResourceEx((IntPtr)pFontData, (uint)Properties.Resources.Arial_Unicode_MS.Length, IntPtr.Zero, ref dummy);
                    }
                }
            }
            catch
            {

            }
        }

        public FontFamily UnicodeFontFamily
        {
            get
            {
                return MYpfc.Families[0];
            }
        }

        public FontFamily DefaultFontFamily
        {
            get
            {
                return UnicodeFontFamily;
            }
        }


        public void ApplyDefaultFont(Control control)
        {
            control.Font = new Font(DefaultFontFamily, control.Font.Size, control.Font.Style);
        }

        public void ApplyDefaultFont(Form frm)
        {
            foreach (Control c in frm.Controls)
            {
                ApplyDefaultFont(c);
            }
        }
    }
}

3. Now in your Winform's contructor, add in the following line immidiately after the "InitializeComponent();" line:

FontManager.Instance.ApplyDefaultFont(this);

4. In your project property page, click the "Build" tab and check the "Allow unsafe code" option.

Now build and run your application, you should be able to read and display the arabic characters in your text files.

No comments:

Post a Comment