Git init
[external/pango1.0.git] / modules / basic / basic-win32.c
1 /* Pango
2  * basic-win32.c:
3  *
4  * Copyright (C) 1999 Red Hat Software
5  * Copyright (C) 2001 Alexander Larsson
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "config.h"
24
25 #define BASIC_WIN32_DEBUGGING
26
27 #include <math.h>
28 #include <stdlib.h>
29
30 #include <glib.h>
31
32 #include "pangowin32.h"
33 #include "pango-engine.h"
34 #include "pango-utils.h"
35
36 /* No extra fields needed */
37 typedef PangoEngineShape      BasicEngineWin32;
38 typedef PangoEngineShapeClass BasicEngineWin32Class ;
39
40 #define SCRIPT_ENGINE_NAME "BasicScriptEngineWin32"
41
42 static gboolean pango_win32_debug = FALSE;
43
44 #include <usp10.h>
45
46 static gboolean have_uniscribe = FALSE;
47
48 static HDC hdc;
49
50 typedef HRESULT (WINAPI *pScriptGetProperties) (const SCRIPT_PROPERTIES ***,
51                                                 int *);
52
53 typedef HRESULT (WINAPI *pScriptItemize) (const WCHAR *,
54                                           int,
55                                           int,
56                                           const SCRIPT_CONTROL *,
57                                           const SCRIPT_STATE *,
58                                           SCRIPT_ITEM *,
59                                           int *);
60
61 typedef HRESULT (WINAPI *pScriptShape) (HDC,
62                                         SCRIPT_CACHE *,
63                                         const WCHAR *,
64                                         int,
65                                         int,
66                                         SCRIPT_ANALYSIS *,
67                                         WORD *,
68                                         WORD *,
69                                         SCRIPT_VISATTR *,
70                                         int *);
71
72 typedef HRESULT (WINAPI *pScriptPlace) (HDC,
73                                         SCRIPT_CACHE *,
74                                         const WORD *,
75                                         int,
76                                         const SCRIPT_VISATTR *,
77                                         SCRIPT_ANALYSIS *,
78                                         int *,
79                                         GOFFSET *,
80                                         ABC *);
81
82 typedef HRESULT (WINAPI *pScriptFreeCache) (SCRIPT_CACHE *);
83
84 typedef HRESULT (WINAPI *pScriptIsComplex) (WCHAR *,
85                                             int,
86                                             DWORD);
87
88 static pScriptGetProperties script_get_properties;
89 static pScriptItemize script_itemize;
90 static pScriptShape script_shape;
91 static pScriptPlace script_place;
92 static pScriptFreeCache script_free_cache;
93 static pScriptIsComplex script_is_complex;
94
95 #ifdef BASIC_WIN32_DEBUGGING
96 static const SCRIPT_PROPERTIES **scripts;
97 static int nscripts;
98 #endif
99
100 static PangoEngineScriptInfo uniscribe_scripts[] = {
101   /* We claim to cover everything ;-) */
102   { PANGO_SCRIPT_COMMON,  "" },
103 };
104
105 static PangoEngineScriptInfo basic_scripts[] = {
106   /* Those characters that can be rendered legibly without Uniscribe.
107    * I am not certain this list is correct.
108    */
109   { PANGO_SCRIPT_ARMENIAN, "*" },
110   { PANGO_SCRIPT_BOPOMOFO, "*" },
111   { PANGO_SCRIPT_CHEROKEE, "*" },
112   { PANGO_SCRIPT_COPTIC,   "*" },
113   { PANGO_SCRIPT_CYRILLIC, "*" },
114   { PANGO_SCRIPT_DESERET,  "*" },
115   { PANGO_SCRIPT_ETHIOPIC, "*" },
116   { PANGO_SCRIPT_GEORGIAN, "*" },
117   { PANGO_SCRIPT_GOTHIC,   "*" },
118   { PANGO_SCRIPT_GREEK,    "*" },
119   { PANGO_SCRIPT_HAN,      "*" },
120   { PANGO_SCRIPT_HANGUL,   "*" },
121   { PANGO_SCRIPT_HIRAGANA, "*" },
122   { PANGO_SCRIPT_KATAKANA, "*" },
123   { PANGO_SCRIPT_LATIN,    "*" },
124   { PANGO_SCRIPT_OGHAM,    "*" },
125   { PANGO_SCRIPT_OLD_ITALIC, "*" },
126   { PANGO_SCRIPT_RUNIC,     "*" },
127   { PANGO_SCRIPT_THAI,      "*" },
128   { PANGO_SCRIPT_CANADIAN_ABORIGINAL, "*" },
129   { PANGO_SCRIPT_YI,       "*" },
130   { PANGO_SCRIPT_BRAILLE,  "*" },
131   { PANGO_SCRIPT_CYPRIOT,  "*" },
132   { PANGO_SCRIPT_LIMBU,    "*" },
133   { PANGO_SCRIPT_OSMANYA,  "*" },
134   { PANGO_SCRIPT_SHAVIAN,  "*" },
135   { PANGO_SCRIPT_LINEAR_B, "*" },
136   { PANGO_SCRIPT_UGARITIC, "*" },
137
138   /* Claim to handle everything as a fallback */
139   { PANGO_SCRIPT_COMMON,   "" }
140 };
141
142 static PangoEngineInfo script_engines[] = {
143   {
144     SCRIPT_ENGINE_NAME,
145     PANGO_ENGINE_TYPE_SHAPE,
146     PANGO_RENDER_TYPE_WIN32,
147     NULL, 0
148   }
149 };
150
151 static PangoGlyph
152 find_char (PangoFont *font,
153            gunichar   wc)
154 {
155   return pango_win32_font_get_glyph_index (font, wc);
156 }
157
158 static void
159 set_glyph (PangoFont        *font,
160            PangoGlyphString *glyphs,
161            int               i,
162            int               offset,
163            PangoGlyph        glyph)
164 {
165   PangoRectangle logical_rect;
166
167   glyphs->glyphs[i].glyph = glyph;
168
169   glyphs->glyphs[i].geometry.x_offset = 0;
170   glyphs->glyphs[i].geometry.y_offset = 0;
171
172   glyphs->log_clusters[i] = offset;
173
174   pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, NULL, &logical_rect);
175   glyphs->glyphs[i].geometry.width = logical_rect.width;
176 }
177
178 static void
179 swap_range (PangoGlyphString *glyphs,
180             int               start,
181             int               end)
182 {
183   int i, j;
184
185   for (i = start, j = end - 1; i < j; i++, j--)
186     {
187       PangoGlyphInfo glyph_info;
188       gint log_cluster;
189
190       glyph_info = glyphs->glyphs[i];
191       glyphs->glyphs[i] = glyphs->glyphs[j];
192       glyphs->glyphs[j] = glyph_info;
193
194       log_cluster = glyphs->log_clusters[i];
195       glyphs->log_clusters[i] = glyphs->log_clusters[j];
196       glyphs->log_clusters[j] = log_cluster;
197     }
198 }
199
200 #ifdef BASIC_WIN32_DEBUGGING
201
202 static char *
203 lang_name (int lang)
204 {
205   LCID lcid = MAKELCID (lang, SORT_DEFAULT);
206   static char retval[10];
207
208   if (!GetLocaleInfo (lcid, LOCALE_SISO639LANGNAME, retval, G_N_ELEMENTS (retval)))
209     sprintf (retval, "%#02x", lang);
210
211   return retval;
212 }
213
214 #endif /* BASIC_WIN32_DEBUGGING */
215
216 static WORD
217 make_langid (PangoLanguage *lang)
218 {
219 #define CASE(t,p,s) if (pango_language_matches (lang, t)) return MAKELANGID (LANG_##p, SUBLANG_##p##_##s)
220 #define CASEN(t,p) if (pango_language_matches (lang, t)) return MAKELANGID (LANG_##p, SUBLANG_NEUTRAL)
221
222   /* Languages that most probably don't affect Uniscribe have been
223    * left out. Uniscribe is documented to use
224    * SCRIPT_CONTROL::uDefaultLanguage only to select digit shapes, so
225    * just leave languages with own digits.
226    */
227
228   CASEN ("ar", ARABIC);
229   CASEN ("hy", ARMENIAN);
230   CASEN ("as", ASSAMESE);
231   CASEN ("az", AZERI);
232   CASEN ("bn", BENGALI);
233   CASE ("zh-tw", CHINESE, TRADITIONAL);
234   CASE ("zh-cn", CHINESE, SIMPLIFIED);
235   CASE ("zh-hk", CHINESE, HONGKONG);
236   CASE ("zh-sg", CHINESE, SINGAPORE);
237   CASE ("zh-mo", CHINESE, MACAU);
238   CASEN ("dib", DIVEHI);
239   CASEN ("fa", FARSI);
240   CASEN ("ka", GEORGIAN);
241   CASEN ("gu", GUJARATI);
242   CASEN ("he", HEBREW);
243   CASEN ("hi", HINDI);
244   CASEN ("ja", JAPANESE);
245   CASEN ("kn", KANNADA);
246   CASE ("ks-in", KASHMIRI, INDIA);
247   CASEN ("ks", KASHMIRI);
248   CASEN ("kk", KAZAK);
249   CASEN ("kok", KONKANI);
250   CASEN ("ko", KOREAN);
251   CASEN ("ky", KYRGYZ);
252   CASEN ("ml", MALAYALAM);
253   CASEN ("mni", MANIPURI);
254   CASEN ("mr", MARATHI);
255   CASEN ("mn", MONGOLIAN);
256   CASE ("ne-in", NEPALI, INDIA);
257   CASEN ("ne", NEPALI);
258   CASEN ("or", ORIYA);
259   CASEN ("pa", PUNJABI);
260   CASEN ("sa", SANSKRIT);
261   CASEN ("sd", SINDHI);
262   CASEN ("syr", SYRIAC);
263   CASEN ("ta", TAMIL);
264   CASEN ("tt", TATAR);
265   CASEN ("te", TELUGU);
266   CASEN ("th", THAI);
267   CASE ("ur-pk", URDU, PAKISTAN);
268   CASE ("ur-in", URDU, INDIA);
269   CASEN ("ur", URDU);
270   CASEN ("uz", UZBEK);
271
272 #undef CASE
273 #undef CASEN
274
275   return MAKELANGID (LANG_NEUTRAL, SUBLANG_NEUTRAL);
276 }
277
278 #ifdef BASIC_WIN32_DEBUGGING
279
280 static void
281 dump_glyphs_and_log_clusters (gboolean rtl,
282                               int      itemlen,
283                               int      charix0,
284                               WORD    *log_clusters,
285                               WORD    *iglyphs,
286                               int      nglyphs)
287 {
288   if (pango_win32_debug)
289   {
290     int j, k, nclusters, clusterix, charix, ng;
291
292     g_print ("  ScriptShape: nglyphs=%d: ", nglyphs);
293
294     for (j = 0; j < nglyphs; j++)
295       g_print ("%d%s", iglyphs[j], (j < nglyphs-1) ? "," : "");
296     g_print ("\n");
297
298     g_print ("  log_clusters: ");
299     for (j = 0; j < itemlen; j++)
300       g_print ("%d ", log_clusters[j]);
301     g_print ("\n");
302     nclusters = 0;
303     for (j = 0; j < itemlen; j++)
304       {
305         if (j == 0 || log_clusters[j-1] != log_clusters[j])
306           nclusters++;
307       }
308     g_print ("  %d clusters:\n", nclusters);
309
310     /* If RTL, first char is the last in the run, otherwise the
311      * first.
312      */
313     clusterix = 0;
314     if (rtl)
315       {
316         int firstglyphix = 0;
317         for (j = itemlen - 1, charix = charix0 + j; j >= 0; j--, charix--)
318           {
319             if (j == itemlen - 1 || log_clusters[j] != log_clusters[j+1])
320               g_print ("  Cluster %d: chars %d--",
321                        clusterix, charix);
322             if (j == 0 || log_clusters[j-1] != log_clusters[j])
323               {
324                 ng = log_clusters[j] - firstglyphix + 1;
325                 g_print ("%d: %d glyphs: ",
326                          charix, ng);
327                 for (k = firstglyphix; k <= log_clusters[j]; k++)
328                   {
329                     g_print ("%d", iglyphs[k]);
330                     if (k < log_clusters[j])
331                       g_print (",");
332                   }
333                 firstglyphix = log_clusters[j] + 1;
334                 clusterix++;
335                 g_print ("\n");
336               }
337           }
338       }
339     else
340       {
341         for (j = 0, charix = charix0; j < itemlen; j++, charix++)
342           {
343             if (j == 0 || log_clusters[j-1] != log_clusters[j])
344               g_print ("  Cluster %d: wchar_t %d--",
345                        clusterix, charix);
346             if (j == itemlen - 1 || log_clusters[j] != log_clusters[j+1])
347               {
348                 int klim = ((j == itemlen-1) ? nglyphs : log_clusters[j+1]);
349                 ng = klim - log_clusters[j];
350                 g_print ("%d: %d glyphs: ",
351                          charix, ng);
352                 for (k = log_clusters[j]; k < klim; k++)
353                   {
354                     g_print ("%d", iglyphs[k]);
355                     if (k != klim - 1)
356                       g_print (",");
357                   }
358                 clusterix++;
359                 g_print ("\n");
360               }
361           }
362       }
363   }
364 }
365
366 #endif /* BASIC_WIN32_DEBUGGING */
367
368 static int
369 unichar_index (wchar_t *wtext,
370                int      ix)
371 {
372   int i, index;
373
374   index = 0;
375   for (i = 0; i < ix; i++)
376     /* Ignore the low surrogate */
377     if (!(wtext[i] >= 0xDC00 && wtext[i] < 0xE000))
378       index++;
379
380   return index;
381 }
382
383 static void
384 set_up_pango_log_clusters (wchar_t *wtext,
385                            gboolean rtl,
386                            int      itemlen,
387                            WORD    *usp_log_clusters,
388                            int      nglyphs,
389                            gint    *pango_log_clusters,
390                            int      char_offset)
391 {
392   int j, k;
393   int first_char_in_cluster;
394
395   if (rtl)
396     {
397       /* RTL. Walk Uniscribe log_clusters array backwards, build Pango
398        * log_clusters array forwards.
399        */
400       int glyph0 = 0;
401       first_char_in_cluster = unichar_index (wtext, itemlen - 1);
402       for (j = itemlen - 1; j >= 0; j--)
403         {
404           if (j < itemlen - 1 && usp_log_clusters[j+1] != usp_log_clusters[j])
405             {
406               /* Cluster starts */
407               first_char_in_cluster = unichar_index (wtext, j);
408             }
409           if (j == 0)
410             {
411               /* First char, cluster ends */
412               for (k = glyph0; k < nglyphs; k++)
413                 pango_log_clusters[k] = first_char_in_cluster + char_offset;
414             }
415           else if (usp_log_clusters[j-1] == usp_log_clusters[j])
416             {
417               /* Cluster continues */
418               first_char_in_cluster = unichar_index (wtext, j-1);
419             }
420           else
421             {
422               /* Cluster ends */
423               for (k = glyph0; k <= usp_log_clusters[j]; k++)
424                 pango_log_clusters[k] = first_char_in_cluster + char_offset;
425               glyph0 = usp_log_clusters[j] + 1;
426             }
427         }
428     }
429   else
430     {
431       /* LTR. Walk Uniscribe log_clusters array forwards, build Pango
432        * log_clusters array also forwards.
433        */
434       first_char_in_cluster = 0;
435       for (j = 0; j < itemlen; j++)
436         {
437           if (j > 0 && usp_log_clusters[j-1] != usp_log_clusters[j])
438             {
439               /* Cluster starts */
440               first_char_in_cluster = unichar_index (wtext, j);
441             }
442           if (j == itemlen - 1)
443             {
444               /* Last char, cluster ends */
445               for (k = usp_log_clusters[j]; k < nglyphs; k++)
446                 pango_log_clusters[k] = first_char_in_cluster + char_offset;
447             }
448           else if (usp_log_clusters[j] == usp_log_clusters[j+1])
449             {
450               /* Cluster continues */
451             }
452           else
453             {
454               /* Cluster ends */
455               for (k = usp_log_clusters[j]; k < usp_log_clusters[j+1]; k++)
456                 pango_log_clusters[k] = first_char_in_cluster + char_offset;
457             }
458         }
459     }
460 }
461
462 static void
463 convert_log_clusters_to_byte_offsets (const char       *text,
464                                       gint              length,
465                                       PangoGlyphString *glyphs)
466 {
467   const char *p;
468   int charix, glyphix;
469   int n_chars = g_utf8_strlen (text, length);
470   int *byte_offset = g_new (int, n_chars);
471
472   p = text;
473   charix = 0;
474   while (p < text + length)
475     {
476       byte_offset[charix] = p - text;
477       charix++;
478       p = g_utf8_next_char (p);
479     }
480
481   /* Convert char indexes in the log_clusters array to byte offsets.
482    */
483   for (glyphix = 0; glyphix < glyphs->num_glyphs; glyphix++)
484     {
485       g_assert (glyphs->log_clusters[glyphix] < n_chars);
486       glyphs->log_clusters[glyphix] = byte_offset[glyphs->log_clusters[glyphix]];
487     }
488
489   g_free (byte_offset);
490 }
491
492 static gboolean
493 itemize_shape_and_place (PangoFont           *font,
494                          HDC                  hdc,
495                          wchar_t             *wtext,
496                          int                  wlen,
497                          const PangoAnalysis *analysis,
498                          PangoGlyphString    *glyphs,
499                          SCRIPT_CACHE        *script_cache)
500 {
501   int i;
502   int item, nitems, item_step;
503   int itemlen, glyphix, nglyphs;
504   SCRIPT_CONTROL control;
505   SCRIPT_STATE state;
506   SCRIPT_ITEM items[100];
507   double scale = pango_win32_font_get_metrics_factor (font);
508
509   memset (&control, 0, sizeof (control));
510   memset (&state, 0, sizeof (state));
511
512   control.uDefaultLanguage = make_langid (analysis->language);
513   state.uBidiLevel = analysis->level;
514
515 #ifdef BASIC_WIN32_DEBUGGING
516   if (pango_win32_debug)
517     g_print (G_STRLOC ": ScriptItemize: uDefaultLanguage:%04x uBidiLevel:%d\n",
518              control.uDefaultLanguage, state.uBidiLevel);
519 #endif
520   if ((*script_itemize) (wtext, wlen, G_N_ELEMENTS (items), &control, NULL,
521                          items, &nitems))
522     {
523 #ifdef BASIC_WIN32_DEBUGGING
524       if (pango_win32_debug)
525         g_print ("ScriptItemize failed\n");
526 #endif
527       return FALSE;
528     }
529
530 #ifdef BASIC_WIN32_DEBUGGING
531   if (pango_win32_debug)
532     g_print ("%d items:\n", nitems);
533 #endif
534
535   if (analysis->level % 2)
536     {
537       item = nitems - 1;
538       item_step = -1;
539     }
540   else
541     {
542       item = 0;
543       item_step = 1;
544     }
545
546   for (i = 0; i < nitems; i++, item += item_step)
547     {
548       WORD iglyphs[1000];
549       WORD log_clusters[1000];
550       SCRIPT_VISATTR visattrs[1000];
551       int advances[1000];
552       GOFFSET offsets[1000];
553       ABC abc;
554       int script = items[item].a.eScript;
555       int ng;
556       int char_offset;
557
558       memset (advances, 0, sizeof (advances));
559       memset (offsets, 0, sizeof (offsets));
560       memset (&abc, 0, sizeof (abc));
561
562       /* Note that itemlen is number of wchar_t's i.e. surrogate pairs
563        * count as two!
564        */
565       itemlen = items[item+1].iCharPos - items[item].iCharPos;
566       char_offset = unichar_index (wtext, items[item].iCharPos);
567
568 #ifdef BASIC_WIN32_DEBUGGING
569       if (pango_win32_debug)
570         g_print ("  Item %d: iCharPos=%d eScript=%d (%s) %s%s%s%s%s%s%s wchar_t %d--%d (%d)\n",
571                  item, items[item].iCharPos, script,
572                  lang_name (scripts[script]->langid),
573                  scripts[script]->fComplex ? "complex" : "simple",
574                  items[item].a.fRTL ? " fRTL" : "",
575                  items[item].a.fLayoutRTL ? " fLayoutRTL" : "",
576                  items[item].a.fLinkBefore ? " fLinkBefore" : "",
577                  items[item].a.fLinkAfter ? " fLinkAfter" : "",
578                  items[item].a.fLogicalOrder ? " fLogicalOrder" : "",
579                  items[item].a.fNoGlyphIndex ? " fNoGlyphIndex" : "",
580                  items[item].iCharPos, items[item+1].iCharPos-1, itemlen);
581 #endif
582
583       items[item].a.fRTL = analysis->level % 2;
584       if ((*script_shape) (hdc, &script_cache[script],
585                            wtext + items[item].iCharPos, itemlen,
586                            G_N_ELEMENTS (iglyphs),
587                            &items[item].a,
588                            iglyphs,
589                            log_clusters,
590                            visattrs,
591                            &nglyphs))
592         {
593 #ifdef BASIC_WIN32_DEBUGGING
594           if (pango_win32_debug)
595             g_print ("pango-basic-win32: ScriptShape failed\n");
596 #endif
597           return FALSE;
598         }
599
600 #ifdef BASIC_WIN32_DEBUGGING
601       dump_glyphs_and_log_clusters (items[item].a.fRTL, itemlen,
602                                     items[item].iCharPos, log_clusters,
603                                     iglyphs, nglyphs);
604 #endif
605
606       ng = glyphs->num_glyphs;
607       pango_glyph_string_set_size (glyphs, ng + nglyphs);
608
609       set_up_pango_log_clusters (wtext + items[item].iCharPos,
610                                  items[item].a.fRTL, itemlen, log_clusters,
611                                  nglyphs, glyphs->log_clusters + ng,
612                                  char_offset);
613
614       if ((*script_place) (hdc, &script_cache[script], iglyphs, nglyphs,
615                            visattrs, &items[item].a,
616                            advances, offsets, &abc))
617         {
618 #ifdef BASIC_WIN32_DEBUGGING
619           if (pango_win32_debug)
620             g_print ("pango-basic-win32: ScriptPlace failed\n");
621 #endif
622           return FALSE;
623         }
624
625       for (glyphix = 0; glyphix < nglyphs; glyphix++)
626         {
627           if (iglyphs[glyphix] != 0)
628             {
629               glyphs->glyphs[ng+glyphix].glyph = iglyphs[glyphix];
630               glyphs->glyphs[ng+glyphix].geometry.width = floor (0.5 + scale * advances[glyphix]);
631               glyphs->glyphs[ng+glyphix].geometry.x_offset = floor (0.5 + scale * offsets[glyphix].du);
632               glyphs->glyphs[ng+glyphix].geometry.y_offset = floor (0.5 + scale * offsets[glyphix].dv);
633             }
634           else
635             {
636               PangoRectangle logical_rect;
637               /* Should pass actual char that was not found to
638                * PANGO_GET_UNKNOWN_GLYPH(), but a bit hard to
639                * find out that at this point, so cheat and use 0.
640                */
641               PangoGlyph unk = PANGO_GET_UNKNOWN_GLYPH (0);
642
643               glyphs->glyphs[ng+glyphix].glyph = unk;
644               pango_font_get_glyph_extents (font, unk, NULL, &logical_rect);
645               glyphs->glyphs[ng+glyphix].geometry.width = logical_rect.width;
646               glyphs->glyphs[ng+glyphix].geometry.x_offset = 0;
647               glyphs->glyphs[ng+glyphix].geometry.y_offset = 0;
648             }
649         }
650     }
651
652 #ifdef BASIC_WIN32_DEBUGGING
653   if (pango_win32_debug)
654     {
655       g_print ("  Pango log_clusters (level:%d), char index:", analysis->level);
656       for (glyphix = 0; glyphix < glyphs->num_glyphs; glyphix++)
657         g_print ("%d ", glyphs->log_clusters[glyphix]);
658       g_print ("\n");
659     }
660 #endif
661
662   return TRUE;
663 }
664
665 static gboolean
666 uniscribe_shape (PangoFont           *font,
667                  const char          *text,
668                  gint                 length,
669                  const PangoAnalysis *analysis,
670                  PangoGlyphString    *glyphs)
671 {
672   wchar_t *wtext;
673   long wlen;
674   int i;
675   gboolean retval = TRUE;
676   SCRIPT_CACHE script_cache[100];
677
678   if (!pango_win32_font_select_font (font, hdc))
679     return FALSE;
680
681   wtext = g_utf8_to_utf16 (text, length, NULL, &wlen, NULL);
682   if (wtext == NULL)
683     retval = FALSE;
684
685   if (retval)
686     {
687       memset (script_cache, 0, sizeof (script_cache));
688       retval = itemize_shape_and_place (font, hdc, wtext, wlen, analysis, glyphs, script_cache);
689       for (i = 0; i < G_N_ELEMENTS (script_cache); i++)
690         if (script_cache[i])
691           (*script_free_cache)(&script_cache[i]);
692     }
693
694   if (retval)
695     {
696       convert_log_clusters_to_byte_offsets (text, length, glyphs);
697 #ifdef BASIC_WIN32_DEBUGGING
698       if (pango_win32_debug)
699         {
700           int glyphix;
701
702           g_print ("  Pango log_clusters, byte offsets:");
703           for (glyphix = 0; glyphix < glyphs->num_glyphs; glyphix++)
704             g_print ("%d ", glyphs->log_clusters[glyphix]);
705           g_print ("\n");
706         }
707 #endif
708     }
709
710   pango_win32_font_done_font (font);
711
712   g_free (wtext);
713
714   return retval && glyphs->num_glyphs > 0;
715 }
716
717 static gboolean
718 text_is_simple (const char *text,
719                 gint        length)
720 {
721   gboolean retval;
722   wchar_t *wtext;
723   long wlen;
724
725   wtext = (wchar_t *) g_utf8_to_utf16 (text, length, NULL, &wlen, NULL);
726   if (wtext == NULL)
727     return TRUE;
728
729   retval = ((*script_is_complex) (wtext, wlen, SIC_COMPLEX) == S_FALSE);
730
731   g_free (wtext);
732
733 #ifdef BASIC_WIN32_DEBUGGING
734   if (pango_win32_debug)
735     g_print ("text_is_simple: %.*s (%ld wchar_t): %s\n",
736              MIN (length, 10), text, wlen, retval ? "YES" : "NO");
737 #endif
738
739   return retval;
740 }
741
742 static void
743 basic_engine_shape (PangoEngineShape    *engine,
744                     PangoFont           *font,
745                     const char          *text,
746                     int                  length,
747                     const PangoAnalysis *analysis,
748                     PangoGlyphString    *glyphs)
749 {
750   int n_chars;
751   int i;
752   const char *p;
753
754   g_return_if_fail (font != NULL);
755   g_return_if_fail (text != NULL);
756   g_return_if_fail (length >= 0);
757   g_return_if_fail (analysis != NULL);
758
759   if (have_uniscribe &&
760       !text_is_simple (text, length) &&
761       uniscribe_shape (font, text, length, analysis, glyphs))
762     return;
763
764   n_chars = g_utf8_strlen (text, length);
765
766   pango_glyph_string_set_size (glyphs, n_chars);
767
768   p = text;
769   for (i = 0; i < n_chars; i++)
770     {
771       gunichar wc;
772       gunichar mirrored_ch;
773       PangoGlyph index;
774
775       wc = g_utf8_get_char (p);
776
777       if (analysis->level % 2)
778         if (pango_get_mirror_char (wc, &mirrored_ch))
779           wc = mirrored_ch;
780
781       if (wc == 0xa0)   /* non-break-space */
782         wc = 0x20;
783
784       if (pango_is_zero_width (wc))
785         {
786           set_glyph (font, glyphs, i, p - text, PANGO_GLYPH_EMPTY);
787         }
788       else
789         {
790           index = find_char (font, wc);
791           if (index)
792             {
793               set_glyph (font, glyphs, i, p - text, index);
794
795               if (g_unichar_type (wc) == G_UNICODE_NON_SPACING_MARK)
796                 {
797                   if (i > 0)
798                     {
799                       PangoRectangle logical_rect, ink_rect;
800
801                       glyphs->glyphs[i].geometry.width = MAX (glyphs->glyphs[i-1].geometry.width,
802                                                               glyphs->glyphs[i].geometry.width);
803                       glyphs->glyphs[i-1].geometry.width = 0;
804                       glyphs->log_clusters[i] = glyphs->log_clusters[i-1];
805
806                       /* Some heuristics to try to guess how overstrike glyphs are
807                        * done and compensate
808                        */
809                       /* FIXME: (alex) Is this double call to get_glyph_extents really necessary? */
810                       pango_font_get_glyph_extents (font, glyphs->glyphs[i].glyph, &ink_rect, &logical_rect);
811                       if (logical_rect.width == 0 && ink_rect.x == 0)
812                         glyphs->glyphs[i].geometry.x_offset = (glyphs->glyphs[i].geometry.width - ink_rect.width) / 2;
813                     }
814                 }
815             }
816           else
817             set_glyph (font, glyphs, i, p - text, PANGO_GET_UNKNOWN_GLYPH (wc));
818         }
819
820       p = g_utf8_next_char (p);
821     }
822
823   /* Simple bidi support... may have separate modules later */
824
825   if (analysis->level % 2)
826     {
827       int start, end;
828
829       /* Swap all glyphs */
830       swap_range (glyphs, 0, n_chars);
831
832       /* Now reorder glyphs within each cluster back to LTR */
833       for (start = 0; start < n_chars;)
834         {
835           end = start;
836           while (end < n_chars &&
837                  glyphs->log_clusters[end] == glyphs->log_clusters[start])
838             end++;
839
840           swap_range (glyphs, start, end);
841           start = end;
842         }
843     }
844 }
845
846 static void
847 init_uniscribe (void)
848 {
849   HMODULE usp10_dll;
850
851   have_uniscribe = FALSE;
852
853   if ((usp10_dll = LoadLibrary ("usp10.dll")) != NULL)
854     {
855       (script_get_properties = (pScriptGetProperties)
856        GetProcAddress (usp10_dll, "ScriptGetProperties")) &&
857       (script_itemize = (pScriptItemize)
858        GetProcAddress (usp10_dll, "ScriptItemize")) &&
859       (script_shape = (pScriptShape)
860        GetProcAddress (usp10_dll, "ScriptShape")) &&
861       (script_place = (pScriptPlace)
862        GetProcAddress (usp10_dll, "ScriptPlace")) &&
863       (script_free_cache = (pScriptFreeCache)
864        GetProcAddress (usp10_dll, "ScriptFreeCache")) &&
865       (script_is_complex = (pScriptIsComplex)
866        GetProcAddress (usp10_dll, "ScriptIsComplex")) &&
867       (have_uniscribe = TRUE);
868     }
869   if (have_uniscribe)
870     {
871 #ifdef BASIC_WIN32_DEBUGGING
872       (*script_get_properties) (&scripts, &nscripts);
873 #endif
874       hdc = pango_win32_get_dc ();
875     }
876 }
877
878 static void
879 basic_engine_win32_class_init (PangoEngineShapeClass *class)
880 {
881   class->script_shape = basic_engine_shape;
882 }
883
884 PANGO_ENGINE_SHAPE_DEFINE_TYPE (BasicEngineWin32, basic_engine_win32,
885                                 basic_engine_win32_class_init, NULL);
886
887 void
888 PANGO_MODULE_ENTRY(init) (GTypeModule *module)
889 {
890   init_uniscribe ();
891
892   if (pango_win32_get_debug_flag ())
893     pango_win32_debug = TRUE;
894
895   basic_engine_win32_register_type (module);
896 }
897
898 void
899 PANGO_MODULE_ENTRY(exit) (void)
900 {
901 }
902
903 void
904 PANGO_MODULE_ENTRY(list) (PangoEngineInfo **engines,
905                           int              *n_engines)
906 {
907   init_uniscribe ();
908
909   script_engines[0].scripts = basic_scripts;
910   script_engines[0].n_scripts = G_N_ELEMENTS (basic_scripts);
911
912   if (have_uniscribe)
913     {
914 #if 0
915       int i;
916       GArray *ranges = g_array_new (FALSE, FALSE, sizeof (PangoEngineRange));
917
918       /* Walk through scripts supported by the Uniscribe implementation on this
919        * machine, and mark corresponding Unicode ranges.
920        */
921       for (i = 0; i < nscripts; i++)
922         {
923         }
924
925       /* Sort range array */
926       g_array_sort (ranges, compare_range);
927       script_engines[0].ranges = ranges;
928       script_engines[0].n_ranges = ranges->len;
929 #else
930       script_engines[0].scripts = uniscribe_scripts;
931       script_engines[0].n_scripts = G_N_ELEMENTS (uniscribe_scripts);
932 #endif
933     }
934
935   *engines = script_engines;
936   *n_engines = G_N_ELEMENTS (script_engines);
937 }
938
939 PangoEngine *
940 PANGO_MODULE_ENTRY(create) (const char *id)
941 {
942   if (!strcmp (id, SCRIPT_ENGINE_NAME))
943     return g_object_new (basic_engine_win32_type, NULL);
944   else
945     return NULL;
946 }