在多行中正确打印阿拉伯字符串

时间:2016-04-04 02:41:29

标签: c# compact-framework windows-ce

目前我需要在我的项目中处理阿拉伯字符串(اسميمصيرالطفل。منأيبلدأنت)。如果字符串显示在一行中,则会正确显示。

enter image description here

但是,当字体大小更大时,显示(我正在使用标签)是错误的。

enter image description here

从第二行开始打印字符串。我发现在.Net框架中我们可以将抽象字符串 StringFormatFlags.DirectionRightToLeft 一起使用。但是,这在紧凑的框架中不可用。那么,我怎么能在多行中打印阿拉伯字符串?感谢任何建议,谢谢。

3 个答案:

答案 0 :(得分:1)

I am sorry, the following is incorrect: Since WinCE 5 and CompactFramework v 2.0 controls like the Textbox support a RicghtToLeft property (see also http://www.danielmoth.com/Blog/rtl-problem.aspx). So you should ensure you are using CF>=2.0 and WinCE 5 base sdk (ie Windows Mobile 6.x).

Looking at the help for Textbox class, the RightToLeft is marked as NOT AVAILABLE for Compact Framework.

enter image description here

So, you need to write your own DrawText class that splits the words and positions them from right to left.

The native DrawText API supports the uFormat flag DT_RTLREADING (according to the online help for Windows CE 5 Platform Builder):

DT_RTLREADING Layout in right-to-left reading order for bi-directional text when the font selected into the hdc is a Hebrew or Arabic font. The default reading order for all text is left-to-right.

There is also an option DT_WORDBREAK which I would choose for multiline text and a large enough drawing rectangle.

BUT, that gives the following result using two rectangles and two font sizes to force a wordbreak:

enter image description here

As I can not read that I am not sure, but I assume the wordbreak flag does not work correctly. I assume the second line in the upper part has to start from right too.

Native Code for the above:

...
#define ARABIC_TEXT L"اسمي مصير الطفل. من أي بلد أنت"
#define MulDiv(a,b,c)       (((a)*(b))/(c))
...
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
RECT rect;
LOGFONT lf;
HFONT hFontNew, hFontOld;
...
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);

        // TODO: Add any drawing code here...
        // Clear out the lf structure to use when creating the font.
        memset(&lf, 0, sizeof(LOGFONT));
        wsprintf(lf.lfFaceName,L"Arial Unicode MS");

        GetClientRect(hWnd, &rect);

        hFontNew = CreateFontIndirect(&lf);
        hFontOld = (HFONT) SelectObject(hdc, hFontNew);
        rect.bottom=rect.bottom/2;
        lf.lfHeight=-MulDiv(16, GetDeviceCaps(hdc, LOGPIXELSY), 72);
        if(DrawText(hdc, ARABIC_TEXT, -1, &rect, DT_RTLREADING | DT_WORDBREAK)==0){
            DEBUGMSG(1, (L"DrawText failed with %i\n", GetLastError()));
        }

        GetClientRect(hWnd, &rect);
        lf.lfHeight=-MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72);
        hFontNew = CreateFontIndirect(&lf);
        hFontOld = (HFONT) SelectObject(hdc, hFontNew);
        rect.top=rect.bottom/2;
        if(DrawText(hdc, ARABIC_TEXT, -1, &rect, DT_RTLREADING | DT_WORDBREAK)==0){
            DEBUGMSG(1, (L"DrawText failed with %i\n", GetLastError()));
        }

        EndPaint(hWnd, &ps);

        SelectObject(hdc, hFontOld);
        DeleteObject(hFontNew);

        break;

答案 1 :(得分:1)

以下表格是从C#调用DrawText(参见josef的回答)的示例:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        Menu = null;
        DrawText(e.Graphics, "اسمي مصير الطفل. من أي بلد أنت", Font, ClientRectangle);
    }

    private void DrawText(Graphics g, string text, Font font, Rectangle rectangle)
    {
        IntPtr dc = g.GetHdc();
        RECT rect = (RECT)rectangle;
        IntPtr hFont = IntPtr.Zero;
        IntPtr previousFont = IntPtr.Zero;

        try
        {
            hFont = font.ToHfont();
            previousFont = SelectObject(dc, hFont);
            DrawText(dc, text, text.Length, ref rect, DrawTextFlags.RightToLeft | DrawTextFlags.Right | DrawTextFlags.WordBreak);
        }
        finally
        {
            if (previousFont != IntPtr.Zero)
            {
                SelectObject(dc, previousFont);
            }
            if (hFont != IntPtr.Zero)
            {
                DeleteObject(hFont);
            }

            g.ReleaseHdc(dc);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct RECT
    {
        internal int Left;
        internal int Top;
        internal int Right;
        internal int Bottom;

        public static explicit operator RECT(Rectangle rect)
        {
            return new RECT()
            {
                Left = rect.Left,
                Top = rect.Top,
                Right = rect.Right,
                Bottom = rect.Bottom
            };
        }
    }
    [DllImport("coredll.dll", CharSet = CharSet.Unicode)]
    internal static extern int DrawText(IntPtr hdc, string lpStr, int nCount, ref RECT lpRect, DrawTextFlags flags);
    [DllImport("coredll.dll", EntryPoint = "DeleteObject")]
    [return: MarshalAs(UnmanagedType.Bool)]
    internal static extern bool DeleteObject([In] IntPtr hObject);
    [DllImport("coredll.dll")]
    internal static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
    [Flags]
    public enum DrawTextFlags : uint
    {
        /// <summary>
        /// Use default values.
        /// </summary>
        None = 0x00000000,

        /// <summary>
        /// Justifies the text to the top of the rectangle.
        /// </summary>
        Top = 0x00000000,

        /// <summary>
        /// Aligns text to the left.
        /// </summary>
        Left = 0x00000000,

        /// <summary>
        /// Centers text horizontally in the rectangle.
        /// </summary>
        Center = 0x00000001,

        /// <summary>
        /// Aligns text to the right.
        /// </summary>
        Right = 0x00000002,

        /// <summary>
        /// Centers text vertically. This value is used only with the SingleLine value.
        /// </summary>
        VerticalCenter = 0x00000004,

        /// <summary>
        /// Justifies the text to the bottom of the rectangle. This value is used only with the 
        /// SingleLine value.
        /// </summary>
        Bottom = 0x00000008,

        /// <summary>
        /// Breaks words. Lines are automatically broken between words if a word would extend past the 
        /// edge of the rectangle specified by the lpRect parameter. A carriage return-line feed sequence 
        /// also breaks the line. If this is not specified, output is on one line.
        /// </summary>
        WordBreak = 0x00000010,

        /// <summary>
        /// Displays text on a single line only. Carriage returns and line feeds do not break the line.
        /// </summary>
        SingleLine = 0x00000020,

        /// <summary>
        /// Expands tab characters. The default number of characters per tab is eight. 
        /// </summary>
        ExpandTabs = 0x00000040,

        /// <summary>
        /// Sets tab stops. Bits 15-8 (high-order byte of the low-order word) of the uFormat parameter 
        /// specify the number of characters for each tab. The default number of characters per tab is 
        /// eight. 
        /// </summary>
        Tabstop = 0x00000080,

        /// <summary>
        /// Draws without clipping. 
        /// </summary>
        NoClip = 0x00000100,

        /// <summary>
        /// Includes the font external leading in line height. Normally, external leading is not included 
        /// in the height of a line of text.
        /// </summary>
        ExternalLeading = 0x00000200,

        /// <summary>
        /// Determines the width and height of the rectangle. If there are multiple lines of text, DrawText 
        /// uses the width of the rectangle pointed to by the lpRect parameter and extends the base of the 
        /// rectangle to bound the last line of text. If the largest word is wider than the rectangle, the 
        /// width is expanded. If the text is less than the width of the rectangle, the width is reduced. 
        /// If there is only one line of text, DrawText modifies the right side of the rectangle so that it 
        /// bounds the last character in the line. In either case, DrawText returns the height of the 
        /// formatted text but does not draw the text.
        /// </summary>
        CalcRect = 0x00000400,

        /// <summary>
        /// Turns off processing of prefix characters. Normally, DrawText interprets the mnemonic-prefix 
        /// character & as a directive to underscore the character that follows, and the mnemonic-prefix 
        /// characters && as a directive to print a single &. By specifying DT_NOPREFIX, this processing 
        /// is turned off. 
        /// </summary>
        NoPrefix = 0x00000800,

        /// <summary>
        /// Uses the system font to calculate text metrics.
        /// </summary>
        Internal = 0x00001000,

        /// <summary>
        /// Duplicates the text-displaying characteristics of a multiline edit control. Specifically, 
        /// the average character width is calculated in the same manner as for an edit control, and 
        /// the function does not display a partially visible last line.
        /// </summary>
        EditControl = 0x00002000,

        /// <summary>
        /// For displayed text, if the end of a string does not fit in the rectangle, it is truncated 
        /// and ellipses are added. If a word that is not at the end of the string goes beyond the 
        /// limits of the rectangle, it is truncated without ellipses.
        /// </summary>
        EndEllipsis = 0x00008000,

        /// <summary>
        /// Layout in right-to-left reading order for bidirectional text when the font selected into the 
        /// hdc is a Hebrew or Arabic font. The default reading order for all text is left-to-right.
        /// </summary>
        RightToLeft = 0x00020000,

        /// <summary>
        /// Truncates any word that does not fit in the rectangle and adds ellipses. 
        /// </summary>
        WordEllipsis = 0x00040000
    }
}

(如你所见,我忽略了每个原生方法的返回值)

答案 2 :(得分:0)

似乎这个问题没有可靠的解决方案,所以我创建了一个临时的解决方法。我假设我的标签大小是固定的,如果我的字符串大小大于我的标签的宽度,我将它分成两个。我用来分割字符串的方法是找到一个分裂点(目前我在整个字符串长度的0.2处进行分割)。

array(1) { [6]=> array(1) { ["00:01"]=> array(2) { ["area1"]=> int(3) ["area2"]=> int(3) } } }

虽然从右到左打印阿拉伯字符串,但仍然从左到右计算索引。上面的代码段非常严格,只有在您想将字符串拆分为两个时才适用。我相信有很多方法可以改进上述代码,欢迎提出任何意见/建议。