diff options
Diffstat (limited to 'modules/basic/basic-win32.c')
-rw-r--r-- | modules/basic/basic-win32.c | 749 |
1 files changed, 749 insertions, 0 deletions
diff --git a/modules/basic/basic-win32.c b/modules/basic/basic-win32.c index 2c3f4629..c85c5573 100644 --- a/modules/basic/basic-win32.c +++ b/modules/basic/basic-win32.c @@ -20,7 +20,14 @@ * Boston, MA 02111-1307, USA. */ +#include "config.h" + +#define BASIC_WIN32_DEBUGGING + +#include <stdlib.h> + #include <glib.h> + #include "pangowin32.h" #include "pango-engine.h" #include "pango-utils.h" @@ -29,7 +36,89 @@ #define SCRIPT_ENGINE_NAME "BasicScriptEngineWin32" +static gboolean pango_win32_debug = FALSE; + +#ifdef HAVE_USP10_H + +#include "usp10.h" + +/* Some languages missing from mingw ("w32api") headers */ + +#ifndef LANG_INVARIANT +#define LANG_INVARIANT 0x7f +#endif +#ifndef LANG_DIVEHI +#define LANG_DIVEHI 0x65 +#endif +#ifndef LANG_GALICIAN +#define LANG_GALICIAN 0x56 +#endif +#ifndef LANG_KYRGYZ +#define LANG_KYRGYZ 0x40 +#endif +#ifndef LANG_MONGOLIAN +#define LANG_MONGOLIAN 0x50 +#endif +#ifndef LANG_SYRIAC +#define LANG_SYRIAC 0x5a +#endif + +static gboolean have_uniscribe = FALSE; + +static PangoWin32FontCache *font_cache; + +static HDC hdc; + +typedef HRESULT (WINAPI *pScriptGetProperties) (const SCRIPT_PROPERTIES ***, + int *); + +typedef HRESULT (WINAPI *pScriptItemize) (const WCHAR *, + int, + int, + const SCRIPT_CONTROL *, + const SCRIPT_STATE *, + SCRIPT_ITEM *, + int *); + +typedef HRESULT (WINAPI *pScriptShape) (HDC, + SCRIPT_CACHE *, + const WCHAR *, + int, + int, + SCRIPT_ANALYSIS *, + WORD *, + WORD *, + SCRIPT_VISATTR *, + int *); + +typedef HRESULT (WINAPI *pScriptPlace) (HDC, + SCRIPT_CACHE *, + const WORD *, + int, + const SCRIPT_VISATTR *, + SCRIPT_ANALYSIS *, + int *, + GOFFSET *, + ABC *); + +typedef HRESULT (WINAPI *pScriptFreeCache) (SCRIPT_CACHE *); + +static pScriptGetProperties script_get_properties; +static pScriptItemize script_itemize; +static pScriptShape script_shape; +static pScriptPlace script_place; +static pScriptFreeCache script_free_cache; + +static const SCRIPT_PROPERTIES **scripts; +int nscripts; + +#endif + static PangoEngineRange basic_ranges[] = { +#ifdef HAVE_USP10_H + /* We claim to cover everything ;-) */ + { 0x0020, 0xffee, "*" } +#else /* Basic Latin, Latin-1 Supplement, Latin Extended-A, Latin Extended-B, * IPA Extensions */ @@ -96,6 +185,7 @@ static PangoEngineRange basic_ranges[] = { /* Halfwidth and Fullwidth Forms (partly) */ { 0xff00, 0xffe3, "*" } +#endif }; static PangoEngineInfo script_engines[] = { @@ -162,6 +252,617 @@ swap_range (PangoGlyphString *glyphs, } } +#ifdef BASIC_WIN32_DEBUGGING + +static char * +charset_name (int charset) +{ + static char unk[10]; + + switch (charset) + { +#define CASE(n) case n##_CHARSET: return #n + CASE (ANSI); + CASE (DEFAULT); + CASE (SYMBOL); + CASE (SHIFTJIS); + CASE (HANGEUL); + CASE (GB2312); + CASE (CHINESEBIG5); + CASE (OEM); + CASE (JOHAB); + CASE (HEBREW); + CASE (ARABIC); + CASE (GREEK); + CASE (TURKISH); + CASE (VIETNAMESE); + CASE (THAI); + CASE (EASTEUROPE); + CASE (RUSSIAN); + CASE (MAC); + CASE (BALTIC); +#undef CASE + default: + sprintf (unk, "%d", charset); + return unk; + } +} + +static char * +lang_name (int lang) +{ + static char unk[10]; + + switch (PRIMARYLANGID (lang)) + { +#define CASE(n) case LANG_##n: return #n + CASE (NEUTRAL); + CASE (INVARIANT); + CASE (AFRIKAANS); + CASE (ALBANIAN); + CASE (ARABIC); + CASE (ARMENIAN); + CASE (ASSAMESE); + CASE (AZERI); + CASE (BASQUE); + CASE (BELARUSIAN); + CASE (BENGALI); + CASE (BULGARIAN); + CASE (CATALAN); + CASE (CHINESE); + CASE (CROATIAN); + CASE (CZECH); + CASE (DANISH); + CASE (DIVEHI); + CASE (DUTCH); + CASE (ENGLISH); + CASE (ESTONIAN); + CASE (FAEROESE); + CASE (FARSI); + CASE (FINNISH); + CASE (FRENCH); + CASE (GALICIAN); + CASE (GEORGIAN); + CASE (GERMAN); + CASE (GREEK); + CASE (GUJARATI); + CASE (HEBREW); + CASE (HINDI); + CASE (HUNGARIAN); + CASE (ICELANDIC); + CASE (INDONESIAN); + CASE (ITALIAN); + CASE (JAPANESE); + CASE (KANNADA); + CASE (KASHMIRI); + CASE (KAZAK); + CASE (KONKANI); + CASE (KOREAN); + CASE (KYRGYZ); + CASE (LATVIAN); + CASE (LITHUANIAN); + CASE (MACEDONIAN); + CASE (MALAY); + CASE (MALAYALAM); + CASE (MANIPURI); + CASE (MARATHI); + CASE (MONGOLIAN); + CASE (NEPALI); + CASE (NORWEGIAN); + CASE (ORIYA); + CASE (POLISH); + CASE (PORTUGUESE); + CASE (PUNJABI); + CASE (ROMANIAN); + CASE (RUSSIAN); + CASE (SANSKRIT); + CASE (SINDHI); + CASE (SLOVAK); + CASE (SLOVENIAN); + CASE (SPANISH); + CASE (SWAHILI); + CASE (SWEDISH); + CASE (SYRIAC); + CASE (TAMIL); + CASE (TATAR); + CASE (TELUGU); + CASE (THAI); + CASE (TURKISH); + CASE (UKRAINIAN); + CASE (URDU); + CASE (UZBEK); + CASE (VIETNAMESE); +#undef CASE + default: + sprintf (unk, "%#02x", lang); + return unk; + } +} + +#endif /* BASIC_WIN32_DEBUGGING */ + +#ifdef HAVE_USP10_H + +static WORD +make_langid (PangoLanguage *lang) +{ +#define CASE(t,p,s) if (pango_language_matches (lang, t)) return MAKELANGID (LANG_##p, SUBLANG_##p##_##s) +#define CASEN(t,p) if (pango_language_matches (lang, t)) return MAKELANGID (LANG_##p, SUBLANG_NEUTRAL) + + /* Languages that most probably don't affect Uniscribe have been + * left out. Presumably still many of those mentioned below don't + * have any effect either. + */ + + CASEN ("sq", ALBANIAN); + CASEN ("ar", ARABIC); + CASEN ("az", AZERI); + CASEN ("hy", ARMENIAN); + CASEN ("as", ASSAMESE); + CASEN ("az", AZERI); + CASEN ("eu", BASQUE); + CASEN ("be", BELARUSIAN); + CASEN ("bn", BENGALI); + CASEN ("bg", BULGARIAN); + CASEN ("ca", CATALAN); + CASE ("zh-tw", CHINESE, TRADITIONAL); + CASE ("zh-cn", CHINESE, SIMPLIFIED); + CASE ("zh-hk", CHINESE, HONGKONG); + CASE ("zh-sg", CHINESE, SINGAPORE); + CASE ("zh-mo", CHINESE, MACAU); + CASEN ("hr", CROATIAN); + CASE ("sr", SERBIAN, CYRILLIC); + CASEN ("dib", DIVEHI); + CASEN ("fa", FARSI); + CASEN ("ka", GEORGIAN); + CASEN ("el", GREEK); + CASEN ("gu", GUJARATI); + CASEN ("he", HEBREW); + CASEN ("hi", HINDI); + CASEN ("ja", JAPANESE); + CASEN ("kn", KANNADA); + CASE ("ks-in", KASHMIRI, INDIA); + CASEN ("ks", KASHMIRI); + CASEN ("kk", KAZAK); + CASEN ("kok", KONKANI); + CASEN ("ko", KOREAN); + CASEN ("ky", KYRGYZ); + CASEN ("ml", MALAYALAM); + CASEN ("mni", MANIPURI); + CASEN ("mr", MARATHI); + CASEN ("mn", MONGOLIAN); + CASE ("ne-in", NEPALI, INDIA); + CASEN ("ne", NEPALI); + CASEN ("or", ORIYA); + CASEN ("pa", PUNJABI); + CASEN ("sa", SANSKRIT); + CASEN ("sd", SINDHI); + CASEN ("syr", SYRIAC); + CASEN ("ta", TAMIL); + CASEN ("tt", TATAR); + CASEN ("te", TELUGU); + CASEN ("th", THAI); + CASEN ("tr", TURKISH); + CASEN ("uk", UKRAINIAN); + CASE ("ur-pk", URDU, PAKISTAN); + CASE ("ur-in", URDU, INDIA); + CASEN ("ur", URDU); + CASEN ("uz", UZBEK); + CASEN ("vi", VIETNAMESE); + +#undef CASE +#undef CASEN + + return MAKELANGID (LANG_NEUTRAL, SUBLANG_NEUTRAL); +} + +static void +dump_glyphs_and_log_clusters (gboolean rtl, + int itemlen, + int charix0, + WORD *log_clusters, + WORD *iglyphs, + int nglyphs) +{ + if (pango_win32_debug) + { + int j, k, nclusters, clusterix, charix, ng; + + printf (" ScriptShape: nglyphs=%d: ", nglyphs); + + for (j = 0; j < nglyphs; j++) + printf ("%d%s", iglyphs[j], (j < nglyphs-1) ? "," : ""); + printf ("\n"); + + printf (" log_clusters: "); + for (j = 0; j < itemlen; j++) + printf ("%d ", log_clusters[j]); + printf ("\n"); nclusters = 0; + for (j = 0; j < itemlen; j++) + { + if (j == 0 || log_clusters[j-1] != log_clusters[j]) + nclusters++; + } + printf (" %d clusters:\n", nclusters); + + /* If RTL, first char is the last in the run, otherwise the + * first. + */ + clusterix = 0; + if (rtl) + { + int firstglyphix = 0; + for (j = itemlen - 1, charix = charix0 + j; j >= 0; j--, charix--) + { + if (j == itemlen - 1 || log_clusters[j] != log_clusters[j+1]) + printf (" Cluster %d: chars %d--", + clusterix, charix); + if (j == 0 || log_clusters[j-1] != log_clusters[j]) + { + ng = log_clusters[j] - firstglyphix + 1; + printf ("%d: %d glyphs: ", + charix, ng); + for (k = firstglyphix; k <= log_clusters[j]; k++) + { + printf ("%d", iglyphs[k]); + if (k < log_clusters[j]) + printf (","); + } + firstglyphix = log_clusters[j] + 1; + clusterix++; + printf ("\n"); + } + } + } + else + { + for (j = 0, charix = charix0; j < itemlen; j++, charix++) + { + if (j == 0 || log_clusters[j-1] != log_clusters[j]) + printf (" Cluster %d: chars %d--", + clusterix, charix); + if (j == itemlen - 1 || log_clusters[j] != log_clusters[j+1]) + { + int klim = ((j == itemlen-1) ? nglyphs : log_clusters[j+1]); + ng = klim - log_clusters[j]; + printf ("%d: %d glyphs: ", + charix, ng); + for (k = log_clusters[j]; k < klim; k++) + { + printf ("%d", iglyphs[k]); + if (k != klim - 1) + printf (","); + } + clusterix++; + printf ("\n"); + } + } + } + } +} + +static void +set_up_pango_log_clusters (gboolean rtl, + int itemlen, + WORD *usp_log_clusters, + int nglyphs, + gint *pango_log_clusters, + int char_offset) +{ + int j, k; + int first_char_in_cluster; + + if (rtl) + { + /* RTL. Walk Uniscribe log_clusters array backwards, build Pango + * log_clusters array forwards. + */ + int glyph0 = 0; + first_char_in_cluster = itemlen - 1; + for (j = itemlen - 1; j >= 0; j--) + { + if (j < itemlen - 1 && usp_log_clusters[j+1] != usp_log_clusters[j]) + { + /* Cluster starts */ + first_char_in_cluster = j; + } + if (j == 0) + { + /* First char, cluster ends */ + for (k = glyph0; k < nglyphs; k++) + pango_log_clusters[k] = first_char_in_cluster + char_offset; + } + else if (usp_log_clusters[j-1] == usp_log_clusters[j]) + { + /* Cluster continues */ + first_char_in_cluster = j-1; + } + else + { + /* Cluster ends */ + for (k = glyph0; k <= usp_log_clusters[j]; k++) + pango_log_clusters[k] = first_char_in_cluster + char_offset; + glyph0 = usp_log_clusters[j] + 1; + } + } + } + else + { + /* LTR. Walk Uniscribe log_clusters array forwards, build Pango + * log_clusters array also forwards. + */ + first_char_in_cluster = 0; + for (j = 0; j < itemlen; j++) + { + if (j > 0 && usp_log_clusters[j-1] != usp_log_clusters[j]) + { + /* Cluster starts */ + first_char_in_cluster = j; + } + if (j == itemlen - 1) + { + /* Last char, cluster ends */ + for (k = usp_log_clusters[j]; k < nglyphs; k++) + pango_log_clusters[k] = first_char_in_cluster + char_offset; + } + else if (usp_log_clusters[j] == usp_log_clusters[j+1]) + { + /* Cluster continues */ + } + else + { + /* Cluster ends */ + for (k = usp_log_clusters[j]; k < usp_log_clusters[j+1]; k++) + pango_log_clusters[k] = first_char_in_cluster + char_offset; + } + } + } +} + +static void +convert_log_clusters_to_byte_offsets (const char *text, + gint length, + PangoAnalysis *analysis, + PangoGlyphString *glyphs) +{ + const char *p; + int charix, glyphix; + + /* Convert char indexes in the log_clusters array to byte offsets in + * the UTF-8 text. + */ + p = text; + charix = 0; + if (analysis->level % 2) + { + glyphix = glyphs->num_glyphs - 1; + while (p < text + length) + { + while (glyphix >= 0 && + glyphs->log_clusters[glyphix] == charix) + { + glyphs->log_clusters[glyphix] = p - text; + glyphix--; + } + charix++; + p = g_utf8_next_char (p); + } + } + else + { + glyphix = 0; + while (p < text + length) + { + while (glyphix < glyphs->num_glyphs && + glyphs->log_clusters[glyphix] == charix) + { + glyphs->log_clusters[glyphix] = p - text; + glyphix++; + } + charix++; + p = g_utf8_next_char (p); + } + } +} + +static gboolean +itemize_shape_and_place (HDC hdc, + wchar_t *wtext, + int wlen, + PangoAnalysis *analysis, + PangoGlyphString *glyphs, + SCRIPT_CACHE *script_cache) +{ + int item, nitems; + int itemlen, glyphix, nglyphs; + int nc; + SCRIPT_CONTROL control; + SCRIPT_STATE state; + SCRIPT_ITEM items[100]; + + memset (&control, 0, sizeof (control)); + memset (&state, 0, sizeof (state)); + + control.uDefaultLanguage = make_langid (analysis->language); + state.uBidiLevel = analysis->level; + + if ((*script_itemize) (wtext, wlen, G_N_ELEMENTS (items), &control, NULL, + items, &nitems)) + { +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + printf ("pango-basic-win32: ScriptItemize failed\n"); +#endif + return FALSE; + } + +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + printf (G_STRLOC ": ScriptItemize: %d items\n", nitems); +#endif + + nc = 0; + for (item = 0; item < nitems; item++) + { + WORD iglyphs[1000]; + WORD log_clusters[1000]; + SCRIPT_VISATTR visattrs[1000]; + int advances[1000]; + GOFFSET offsets[1000]; + ABC abc; + int script = items[item].a.eScript; + int ng; + + memset (advances, 0, sizeof (advances)); + memset (offsets, 0, sizeof (offsets)); + memset (&abc, 0, sizeof (abc)); + + itemlen = items[item+1].iCharPos - items[item].iCharPos; + +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + printf (" Item %d: iCharPos=%d eScript=%d (%s) %s%s%s%s%s%s%s chars %d--%d (%d)\n", + item, items[item].iCharPos, script, + lang_name (scripts[script]->langid), + scripts[script]->fComplex ? "complex" : "simple", + items[item].a.fRTL ? " fRTL" : "", + items[item].a.fLayoutRTL ? " fLayoutRTL" : "", + items[item].a.fLinkBefore ? " fLinkBefore" : "", + items[item].a.fLinkAfter ? " fLinkAfter" : "", + items[item].a.fLogicalOrder ? " fLogicalOrder" : "", + items[item].a.fNoGlyphIndex ? " fNoGlyphIndex" : "", + items[item].iCharPos, items[item+1].iCharPos-1, itemlen); +#endif + + if ((*script_shape) (hdc, &script_cache[script], wtext + items[item].iCharPos, itemlen, + G_N_ELEMENTS (iglyphs), + &items[item].a, + iglyphs, + log_clusters, + visattrs, + &nglyphs)) + { +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + printf ("pango-basic-win32: ScriptShape failed\n"); +#endif + return FALSE; + } + +#ifdef BASIC_WIN32_DEBUGGING + dump_glyphs_and_log_clusters (items[item].a.fRTL, itemlen, + items[item].iCharPos, log_clusters, + iglyphs, nglyphs); +#endif + + ng = glyphs->num_glyphs; + pango_glyph_string_set_size (glyphs, ng + nglyphs); + + set_up_pango_log_clusters (items[item].a.fRTL, itemlen, log_clusters, + nglyphs, glyphs->log_clusters + ng, nc); + + if ((*script_place) (hdc, &script_cache[script], iglyphs, nglyphs, + visattrs, &items[item].a, + advances, offsets, &abc)) + { +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + printf ("pango-basic-win32: ScriptPlace failed\n"); +#endif + return FALSE; + } + + for (glyphix = 0; glyphix < nglyphs; glyphix++) + { + glyphs->glyphs[ng+glyphix].glyph = iglyphs[glyphix]; + glyphs->glyphs[ng+glyphix].geometry.width = PANGO_SCALE * advances[glyphix]; + glyphs->glyphs[ng+glyphix].geometry.x_offset = PANGO_SCALE * offsets[glyphix].du; + glyphs->glyphs[ng+glyphix].geometry.y_offset = PANGO_SCALE * offsets[glyphix].dv; + } + nc += itemlen; + } + +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + { + printf (" Pango log_clusters, char index:"); + for (glyphix = 0; glyphix < glyphs->num_glyphs; glyphix++) + printf ("%d ", glyphs->log_clusters[glyphix]); + printf ("\n"); + } +#endif + + return TRUE; +} + +static gboolean +uniscribe_shape (PangoFont *font, + const char *text, + gint length, + PangoAnalysis *analysis, + PangoGlyphString *glyphs) +{ + wchar_t *wtext; + int wlen, i; + gboolean retval = TRUE; + GError *error; + HGDIOBJ old_font; + LOGFONT *lf; + SCRIPT_CACHE script_cache[100]; + + wtext = (wchar_t *) g_convert (text, length, "UTF-16LE", "UTF-8", + NULL, &wlen, &error); + if (wtext == NULL) + return FALSE; + + wlen /= 2; + + lf = pango_win32_font_logfont (font); + old_font = SelectObject (hdc, pango_win32_font_cache_load (font_cache, lf)); + if (old_font == NULL) + { +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + printf ("pango-basic-win32: SelectObject for font failed\n"); +#endif + retval = FALSE; + } + + if (retval) + { + memset (script_cache, 0, sizeof (script_cache)); + retval = itemize_shape_and_place (hdc, wtext, wlen, analysis, glyphs, script_cache); + for (i = 0; i < G_N_ELEMENTS (script_cache); i++) + if (script_cache[i]) + (*script_free_cache)(&script_cache[i]); + } + + if (retval) + { + convert_log_clusters_to_byte_offsets (text, length, analysis, glyphs); +#ifdef BASIC_WIN32_DEBUGGING + if (pango_win32_debug) + { + int glyphix; + + printf (" Pango log_clusters, byte offsets:"); + for (glyphix = 0; glyphix < glyphs->num_glyphs; glyphix++) + printf ("%d ", glyphs->log_clusters[glyphix]); + printf ("\n"); + } +#endif + + } + + g_free (wtext); + if (old_font != NULL) + SelectObject (hdc, old_font); + + return retval; +} + +#endif /* HAVE_USP10_H */ + static void basic_engine_shape (PangoFont *font, const char *text, @@ -178,7 +879,15 @@ basic_engine_shape (PangoFont *font, g_return_if_fail (length >= 0); g_return_if_fail (analysis != NULL); +#ifdef HAVE_USP10_H + + if (have_uniscribe && uniscribe_shape (font, text, length, analysis, glyphs)) + return; + +#endif + n_chars = g_utf8_strlen (text, length); + pango_glyph_string_set_size (glyphs, n_chars); p = text; @@ -282,6 +991,41 @@ basic_engine_win32_new (void) return (PangoEngine *)result; } +static void +init_uniscribe (void) +{ +#ifdef HAVE_USP10_H + HMODULE usp10_dll; + + have_uniscribe = FALSE; + + if (getenv ("PANGO_WIN32_NO_UNISCRIBE") == NULL && + (usp10_dll = LoadLibrary ("usp10.dll")) != NULL) + { + (script_get_properties = (pScriptGetProperties) + GetProcAddress (usp10_dll, "ScriptGetProperties")) && + (script_itemize = (pScriptItemize) + GetProcAddress (usp10_dll, "ScriptItemize")) && + (script_shape = (pScriptShape) + GetProcAddress (usp10_dll, "ScriptShape")) && + (script_place = (pScriptPlace) + GetProcAddress (usp10_dll, "ScriptPlace")) && + (script_free_cache = (pScriptFreeCache) + GetProcAddress (usp10_dll, "ScriptFreeCache")) && + (have_uniscribe = TRUE); + } + if (have_uniscribe) + { + (*script_get_properties) (&scripts, &nscripts); + + font_cache = pango_win32_font_map_get_font_cache + (pango_win32_font_map_for_display ()); + + hdc = pango_win32_get_dc (); + } +#endif +} + /* The following three functions provide the public module API for * Pango */ @@ -302,6 +1046,11 @@ MODULE_ENTRY(script_engine_list) (PangoEngineInfo **engines, PangoEngine * MODULE_ENTRY(script_engine_load) (const char *id) { + init_uniscribe (); + + if (pango_win32_get_debug_flag ()) + pango_win32_debug = TRUE; + if (!strcmp (id, SCRIPT_ENGINE_NAME)) return basic_engine_win32_new (); else |