Font don't have none, Uniscribe won't be none (aka Garbage in, exception out)

by Michael S. Kaplan, published on 2007/11/07 10:16 -05:00, original URI: http://blogs.msdn.com/b/michkap/archive/2007/11/07/5952884.aspx


Donovan Smith asked:

How do I troubleshoot ScriptShape returning 80040200?  I am having problems with Telugu consonants in the Arial Unicode MS font.  The glyphs I am attempting to shape are present in the font.

Sample code is below.

Thank you.

Donovan

The actual cause of the problem, that 0x80040200 return value from Uniscribe's ScriptShape, is USP_E_SCRIPT_NOT_IN_FONT. This value is defined in usp10.h in the way that is technifcally correct but which also means you can't search for the value itself:

/////   USP Status Codes
//
#define USP_E_SCRIPT_NOT_IN_FONT   \
        MAKE_HRESULT(SEVERITY_ERROR,FACILITY_ITF,0x200)    // Script doesn't exist in font

In this particular case, as Sergey Malkin pointed out:

Layout tables for Telugu script are required by Uniscribe to process Telugu text. Arial Unicode does not implement layout tables for Telugu, hence USP_E_SCRIPT_NOT_IN_FONT error.

This might be a good time to point out that Arial Unicode MS, while an attractive font to consider using as a way to cover huge pieces of Unicode, is as a rule going to run into many problems if you use it as a first line of defense instead of either

(Whichever of the two is most appropriate -- in this case the first one is best!)

Of course I couldn't resist the chance to include a Uniscribe sample so you can run through the repro for this one yourself, and we never can have enough Uniscribe samples. :-)

For people who want to enjoy a bit of Uniscribe in managed code:

namespace ConsoleApplication1 {
    using System;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Drawing.Text;
    using System.Runtime.InteropServices;

    class Program {
        static void Main(string[] args) {
            try {
                string telugu = "జఛలతబఫఙలసతజదఫలఝచఫభఫధ";
                string gujarati = "પ્રમાણપત્ર";

                PerformTest(telugu, "Tunga");
                PerformTest(gujarati, "Shruti");
                PerformTest(telugu, "Arial Unicode MS");
                PerformTest(gujarati, "Arial Unicode MS");
            } catch (Exception e) {
                Console.WriteLine(e);
            }
        }

        static void PerformTest(String text, String fontName) {
            Console.WriteLine();
            using (Bitmap bitmap = new Bitmap(100, 100)) {
                bitmap.SetResolution(96, 96);
                using (Graphics graphics = Graphics.FromImage(bitmap)) {
                    SCRIPT_ITEM[] items = new SCRIPT_ITEM[text.Length + 1];
                    Int32 itemsCount = 0;
                    SCRIPT_CONTROL scriptControl = new SCRIPT_CONTROL();
                    SCRIPT_STATE scriptState = new SCRIPT_STATE();
                    Int32 hr = Uniscribe.ScriptItemize(text, text.Length, items.Length, ref scriptControl, ref scriptState, items, ref itemsCount);
                    if (hr != Win32.S_OK)
                        throw new Exception("ScriptItemize returned 0x" + hr.ToString("X"));
                    if (itemsCount > 2)
                        throw new Exception(">1 run in test text");

                    Int32 script = items[0].analysis.eScript;
                    Byte charset;
                    if (ScriptProperties.GetProperties(script).IsAmbiguousCharSet)
                        charset = Win32.DEFAULT_CHARSET;
                    else
                        charset = ScriptProperties.GetProperties(script).CharSet;

                    Console.WriteLine("Charset: " + (charset == Win32.DEFAULT_CHARSET ? "DEFAULT_CHARSET" : charset.ToString()));
                    SCRIPT_ANALYSIS scriptAnalysis = new SCRIPT_ANALYSIS();
                    scriptAnalysis.word1 = items[0].analysis.word1;

                    IntPtr hFont = Win32.CreateFont(-(Int32)(10f * graphics.DpiX / 72f), 0, 0, 0, 0, 0, 0, 0, charset, Win32.OUT_TT_ONLY_PRECIS, 0, 0, 0, fontName);
                    IntPtr hdc = graphics.GetHdc();
                    IntPtr oldObject = Win32.SelectObject(hdc, hFont);

                    Console.Write("String: [" + text.Length + "] ");
                    for (Int32 i = 0; i < text.Length; i++)
                        Console.Write(text[i] + "(" + Convert.ToInt32(text[i]) + ") ");
                    Console.WriteLine();

                    IntPtr scriptCache = IntPtr.Zero;
                    Int16[] glyphs = new Int16[text.Length * 3];
                    Int16[] clusters = new Int16[glyphs.Length];
                    SCRIPT_VISATTR[] visAttrs = new SCRIPT_VISATTR[glyphs.Length];
                    Int32 glyphCount = 0;
                    hr = Uniscribe.ScriptShape(hdc, ref scriptCache, text, text.Length, glyphs.Length, ref scriptAnalysis, glyphs, clusters, visAttrs, ref glyphCount);
                    if (hr != Win32.S_OK)
                        throw new Exception("ScriptShape returned 0x" + hr.ToString("X"));
                    Console.Write("Glyph Indices: [" + glyphCount + "] ");
                    for (Int32 i = 0; i < glyphCount; i++)
                        Console.Write(glyphs[i] + " ");
                    Console.WriteLine();
                    Console.Write("Clusters: [" + glyphCount + "] ");
                    for (Int32 i = 0; i < glyphCount; i++)
                        Console.Write(clusters[i] + " ");
                    Console.WriteLine();
                    Uniscribe.ScriptFreeCache(ref scriptCache);

                    Win32.SelectObject(hdc, oldObject);
                    Win32.DeleteObject(hFont);
                    graphics.ReleaseHdc();
                }
            }
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct SCRIPT_ANALYSIS {
        internal UInt16 word1;
        internal SCRIPT_STATE state;

        internal Int32 eScript {
            get { return word1 & 0x03FF; }
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct SCRIPT_ITEM {
        internal Int32 iCharPos;
        internal SCRIPT_ANALYSIS analysis;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct SCRIPT_STATE {
        internal UInt16 word1;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct SCRIPT_CONTROL {
        internal UInt32 dword1;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct SCRIPT_VISATTR {
        internal UInt16 word1;
    }

    internal class ScriptProperties {
        private Int64 m_value;
        internal static ScriptProperties[] m_scriptProperties;

        internal Byte CharSet { get { return (Byte)((m_value >> 20) & 0xff); } }
        internal Boolean IsAmbiguousCharSet { get { return (((m_value >> 34) & 1) > 0); } }

        internal static Int32 Length { get { return m_scriptProperties.Length; } }

        internal ScriptProperties(Int64 value) {
            m_value = value;
        }

        static ScriptProperties() {
            IntPtr ppScriptProperties;
            Int32 numScripts;
            Int32 hr = Uniscribe.ScriptGetProperties(out ppScriptProperties, out numScripts);
            System.Diagnostics.Debug.Assert(hr == Win32.S_OK);

            m_scriptProperties = new ScriptProperties[numScripts];
            for (Int32 i = 0; i < numScripts; i++) {
                IntPtr ptr = Marshal.ReadIntPtr(ppScriptProperties, i * IntPtr.Size);
                long prop = Marshal.ReadInt64(ptr);
                m_scriptProperties[i] = new ScriptProperties(prop);
            }
        }

        internal static ScriptProperties GetProperties(Int32 script) {
            return m_scriptProperties[script];
        }
    }

    internal sealed class Uniscribe {
        [DllImport("usp10.dll")]
        internal static extern Int32 ScriptItemize(
            [MarshalAs(UnmanagedType.LPWStr)] String pwcInChars,
            Int32 cInChars, Int32 cMaxItems, ref SCRIPT_CONTROL psControl, ref SCRIPT_STATE psState,
            [In, Out] SCRIPT_ITEM[] pItems,
            ref Int32 pcItems);

        [DllImport("usp10.dll")]
        internal static extern Int32 ScriptShape(
            IntPtr hdc, ref IntPtr psc,
            [MarshalAs(UnmanagedType.LPWStr)] String pwcChars,
            Int32 cChars, Int32 cMaxGlyphs,
            ref SCRIPT_ANALYSIS psa,
            [Out] [MarshalAs(UnmanagedType.LPArray)] Int16[] pwOutGlyphs,
            [Out] [MarshalAs(UnmanagedType.LPArray)] Int16[] pwLogClust,
            [Out] [MarshalAs(UnmanagedType.LPArray)] SCRIPT_VISATTR[] psva,
            ref Int32 pcGlyphs);

        [DllImport("usp10.dll")]
        internal static extern Int32 ScriptGetProperties(out IntPtr ppScriptProperties, out Int32 pNumScripts);

        [DllImport("usp10.dll")]
        internal static extern Int32 ScriptFreeCache(ref IntPtr psc);
    }

    internal sealed class Win32 {
        internal const Int32 S_OK = 0;
        internal const Int32 DEFAULT_CHARSET = 1;
        internal const Int32 OUT_TT_ONLY_PRECIS = 7;

        [DllImport("gdi32.dll")]
        internal static extern IntPtr SelectObject(IntPtr hDC, IntPtr gdiobj);

        [DllImport("gdi32.dll")]
        internal static extern Int32 DeleteObject(IntPtr hObject);

        [DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
        internal static extern IntPtr CreateFont(Int32 nHeight, Int32 nWidth, Int32 nEscapement, Int32 nOrientation, 
            Int32 fnWeight, UInt32 fdwItalic, UInt32 fdwUnderline, UInt32 fdwStrikeOut, UInt32 fdwCharSet, UInt32 fdwOutputPrecision, UInt32 fdwClipPrecision, UInt32 fdwQuality, UInt32 fdwPitchAndFamily, String lpszFace);
    }
}

Enjoy! :-)

 

This post brought to you by(U+0c1c, a.k.a. TELUGU LETTER JA)


Let Uniscribe decide the font on 11 Aug 2010 9:21 AM:

Hi!

When you say "Letting Uniscribe choose the right font for you", how can that be done? When I have a unicode string in c#, is there a function that will tell me which font should I use to find all glyphs?

thanks!

dan

Michael S. Kaplan on 11 Aug 2010 10:22 AM:

You do not need to specify a font at all; just set compatible text rendering in WinForms to False and Uniscribe will do the work to grab letters as needed....


Please consider a donation to keep this archive running, maintained and free of advertising.
Donate €20 or more to receive an offline copy of the whole archive including all images.

referenced by

2008/09/14 Johab to be kidding me!

go to newer or older post, or back to index or month or day