Git init
[external/pango1.0.git] / pango / pangowin32.c
1 /* Pango
2  * pangowin32.c: Routines for handling Windows fonts
3  *
4  * Copyright (C) 1999 Red Hat Software
5  * Copyright (C) 2000 Tor Lillqvist
6  * Copyright (C) 2001 Alexander Larsson
7  * Copyright (C) 2007 Novell, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 #include "config.h"
26
27 #include <string.h>
28 #include <stdlib.h>
29 #include <glib.h>
30
31 #include "pango-impl-utils.h"
32 #include "pangowin32.h"
33 #include "pangowin32-private.h"
34
35 #define MAX_FREED_FONTS 16
36
37 #define CH_IS_UNIHAN_BMP(ch) ((ch) >= 0x3400 && (ch) <= 0x9FFF)
38 #define CH_IS_UNIHAN(ch) (CH_IS_UNIHAN_BMP (ch) || \
39                           ((ch) >= 0x20000 && (ch) <= 0x2A6DF) ||       \
40                           ((ch) >= 0x2F800 && (ch) <= 0x2FA1F))
41
42 HDC _pango_win32_hdc;
43 OSVERSIONINFO _pango_win32_os_version_info;
44 gboolean _pango_win32_debug = FALSE;
45
46 static void pango_win32_font_dispose    (GObject             *object);
47 static void pango_win32_font_finalize   (GObject             *object);
48
49 static gboolean pango_win32_font_real_select_font        (PangoFont *font,
50                                                           HDC        hdc);
51 static void     pango_win32_font_real_done_font          (PangoFont *font);
52 static double   pango_win32_font_real_get_metrics_factor (PangoFont *font);
53
54 static PangoFontDescription *pango_win32_font_describe          (PangoFont        *font);
55 static PangoFontDescription *pango_win32_font_describe_absolute (PangoFont        *font);
56 static PangoCoverage        *pango_win32_font_get_coverage      (PangoFont        *font,
57                                                                  PangoLanguage    *lang);
58 static void                  pango_win32_font_calc_coverage     (PangoFont        *font,
59                                                                  PangoCoverage    *coverage,
60                                                                  PangoLanguage    *lang);
61 static PangoEngineShape     *pango_win32_font_find_shaper       (PangoFont        *font,
62                                                                  PangoLanguage    *lang,
63                                                                  guint32           ch);
64 static void                  pango_win32_font_get_glyph_extents (PangoFont        *font,
65                                                                  PangoGlyph        glyph,
66                                                                  PangoRectangle   *ink_rect,
67                                                                  PangoRectangle   *logical_rect);
68 static PangoFontMetrics *    pango_win32_font_get_metrics       (PangoFont        *font,
69                                                                  PangoLanguage    *lang);
70 static PangoFontMap *        pango_win32_font_get_font_map      (PangoFont        *font);
71
72 static gboolean pango_win32_font_real_select_font      (PangoFont *font,
73                                                         HDC        hdc);
74 static void     pango_win32_font_real_done_font        (PangoFont *font);
75 static double   pango_win32_font_real_get_metrics_factor (PangoFont *font);
76
77 static HFONT                 pango_win32_get_hfont              (PangoFont        *font);
78 static void                  pango_win32_get_item_properties    (PangoItem        *item,
79                                                                  PangoUnderline   *uline,
80                                                                  PangoAttrColor   *fg_color,
81                                                                  gboolean         *fg_set,
82                                                                  PangoAttrColor   *bg_color,
83                                                                  gboolean         *bg_set);
84
85 static HFONT
86 pango_win32_get_hfont (PangoFont *font)
87 {
88   PangoWin32Font *win32font = (PangoWin32Font *)font;
89   PangoWin32FontCache *cache;
90   TEXTMETRIC tm;
91
92   if (!win32font)
93     return NULL;
94
95   if (!win32font->hfont)
96     {
97       cache = pango_win32_font_map_get_font_cache (win32font->fontmap);
98
99       win32font->hfont = pango_win32_font_cache_loadw (cache, &win32font->logfontw);
100       if (!win32font->hfont)
101         {
102           gchar *face_utf8 = g_utf16_to_utf8 (win32font->logfontw.lfFaceName,
103                                               -1, NULL, NULL, NULL);
104           g_warning ("Cannot load font '%s\n", face_utf8);
105           g_free (face_utf8);
106           return NULL;
107         }
108
109       SelectObject (_pango_win32_hdc, win32font->hfont);
110       GetTextMetrics (_pango_win32_hdc, &tm);
111
112       win32font->tm_overhang = tm.tmOverhang;
113       win32font->tm_descent = tm.tmDescent;
114       win32font->tm_ascent = tm.tmAscent;
115     }
116
117   return win32font->hfont;
118 }
119
120 /**
121  * pango_win32_get_context:
122  *
123  * Retrieves a #PangoContext appropriate for rendering with Windows fonts.
124  *
125  * Return value: the new #PangoContext
126  *
127  * Deprecated: 1.22: Use pango_win32_font_map_for_display() followed by
128  * pango_font_map_create_context() instead.
129  **/
130 PangoContext *
131 pango_win32_get_context (void)
132 {
133   return pango_font_map_create_context (pango_win32_font_map_for_display ());
134 }
135
136 G_DEFINE_TYPE (PangoWin32Font, _pango_win32_font, PANGO_TYPE_FONT)
137
138 static void
139 _pango_win32_font_init (PangoWin32Font *win32font)
140 {
141   win32font->size = -1;
142
143   win32font->metrics_by_lang = NULL;
144
145   win32font->glyph_info = g_hash_table_new_full (NULL, NULL, NULL, g_free);
146 }
147
148 /**
149  * pango_win32_get_dc:
150  *
151  * Obtains a handle to the Windows device context that is used by Pango.
152  *
153  * Return value: A handle to the Windows device context that is used by Pango.
154  **/
155 HDC
156 pango_win32_get_dc (void)
157 {
158   if (_pango_win32_hdc == NULL)
159     {
160       _pango_win32_hdc = CreateDC ("DISPLAY", NULL, NULL, NULL);
161       memset (&_pango_win32_os_version_info, 0,
162               sizeof (_pango_win32_os_version_info));
163       _pango_win32_os_version_info.dwOSVersionInfoSize =
164         sizeof (OSVERSIONINFO);
165       GetVersionEx (&_pango_win32_os_version_info);
166
167       /* Also do some generic pangowin32 initialisations... this function
168        * is a suitable place for those as it is called from a couple
169        * of class_init functions.
170        */
171 #ifdef PANGO_WIN32_DEBUGGING
172       if (getenv ("PANGO_WIN32_DEBUG") != NULL)
173         _pango_win32_debug = TRUE;
174 #endif
175     }
176
177   return _pango_win32_hdc;
178 }
179
180 /**
181  * pango_win32_get_debug_flag:
182  *
183  * Returns whether debugging is turned on.
184  *
185  * Return value: %TRUE if debugging is turned on.
186  *
187  * Since: 1.2
188  */
189 gboolean
190 pango_win32_get_debug_flag (void)
191 {
192   return _pango_win32_debug;
193 }
194
195 static void
196 _pango_win32_font_class_init (PangoWin32FontClass *class)
197 {
198   GObjectClass *object_class = G_OBJECT_CLASS (class);
199   PangoFontClass *font_class = PANGO_FONT_CLASS (class);
200
201   object_class->finalize = pango_win32_font_finalize;
202   object_class->dispose = pango_win32_font_dispose;
203
204   font_class->describe = pango_win32_font_describe;
205   font_class->describe_absolute = pango_win32_font_describe_absolute;
206   font_class->get_coverage = pango_win32_font_get_coverage;
207   font_class->find_shaper = pango_win32_font_find_shaper;
208   font_class->get_glyph_extents = pango_win32_font_get_glyph_extents;
209   font_class->get_metrics = pango_win32_font_get_metrics;
210   font_class->get_font_map = pango_win32_font_get_font_map;
211
212   class->select_font = pango_win32_font_real_select_font;
213   class->done_font = pango_win32_font_real_done_font;
214   class->get_metrics_factor = pango_win32_font_real_get_metrics_factor;
215
216   pango_win32_get_dc ();
217 }
218
219 /**
220  * pango_win32_render:
221  * @hdc:     the device context
222  * @font:    the font in which to draw the string
223  * @glyphs:  the glyph string to draw
224  * @x:       the x position of start of string (in pixels)
225  * @y:       the y position of baseline (in pixels)
226  *
227  * Render a #PangoGlyphString onto a Windows DC
228  */
229 void
230 pango_win32_render (HDC               hdc,
231                     PangoFont        *font,
232                     PangoGlyphString *glyphs,
233                     int               x,
234                     int               y)
235 {
236   HFONT hfont, old_hfont = NULL;
237   int i, j, num_valid_glyphs;
238   guint16 *glyph_indexes;
239   gint *dX;
240   gint this_x;
241   gint start_x_offset, x_offset, next_x_offset, cur_y_offset; /* in Pango units */
242
243   g_return_if_fail (glyphs != NULL);
244
245 #ifdef PANGO_WIN32_DEBUGGING
246   if (_pango_win32_debug)
247     {
248       PING (("num_glyphs:%d", glyphs->num_glyphs));
249       for (i = 0; i < glyphs->num_glyphs; i++)
250         {
251           g_print (" %d:%d", glyphs->glyphs[i].glyph, glyphs->glyphs[i].geometry.width);
252           if (glyphs->glyphs[i].geometry.x_offset != 0 ||
253               glyphs->glyphs[i].geometry.y_offset != 0)
254             g_print (":%d,%d", glyphs->glyphs[i].geometry.x_offset,
255                      glyphs->glyphs[i].geometry.y_offset);
256         }
257       g_print ("\n");
258     }
259 #endif
260
261   if (glyphs->num_glyphs == 0)
262     return;
263
264   hfont = pango_win32_get_hfont (font);
265   if (!hfont)
266     return;
267
268   old_hfont = SelectObject (hdc, hfont);
269
270   glyph_indexes = g_new (guint16, glyphs->num_glyphs);
271   dX = g_new (INT, glyphs->num_glyphs);
272
273   /* Render glyphs using one ExtTextOutW() call for each run of glyphs
274    * that have the same y offset. The big majoroty of glyphs will have
275    * y offset of zero, so in general, the whole glyph string will be
276    * rendered by one call to ExtTextOutW().
277    *
278    * In order to minimize buildup of rounding errors, we keep track of
279    * where the glyphs should be rendered in Pango units, and round
280    * to pixels separately for each glyph,
281    */
282
283   i = 0;
284
285   /* Outer loop through all glyphs in string */
286   while (i < glyphs->num_glyphs)
287     {
288       cur_y_offset = glyphs->glyphs[i].geometry.y_offset;
289       num_valid_glyphs = 0;
290       x_offset = 0;
291       start_x_offset = glyphs->glyphs[i].geometry.x_offset;
292       this_x = PANGO_PIXELS (start_x_offset);
293
294       /* Inner loop through glyphs with the same y offset, or code
295        * point zero (just spacing).
296        */
297       while (i < glyphs->num_glyphs &&
298              (glyphs->glyphs[i].glyph == PANGO_GLYPH_EMPTY ||
299               cur_y_offset == glyphs->glyphs[i].geometry.y_offset))
300         {
301           if (glyphs->glyphs[i].glyph == PANGO_GLYPH_EMPTY)
302             {
303               /* PANGO_GLYPH_EMPTY glyphs should not be rendered, but their
304                * indicated width (set up by PangoLayout) should be taken
305                * into account.
306                */
307
308               /* If the string starts with spacing, must shift the
309                * starting point for the glyphs actually rendered.  For
310                * spacing in the middle of the glyph string, add to the dX
311                * of the previous glyph to be rendered.
312                */
313               if (num_valid_glyphs == 0)
314                 start_x_offset += glyphs->glyphs[i].geometry.width;
315               else
316                 {
317                   x_offset += glyphs->glyphs[i].geometry.width;
318                   dX[num_valid_glyphs-1] = PANGO_PIXELS (x_offset) - this_x;
319                 }
320             }
321           else
322             {
323               if (glyphs->glyphs[i].glyph & PANGO_GLYPH_UNKNOWN_FLAG)
324                 {
325                   /* Glyph index is actually the char value that doesn't
326                    * have any glyph (ORed with the flag). We should really
327                    * do the same that pango_xft_real_render() does: render
328                    * a box with the char value in hex inside it in a tiny
329                    * font. Later. For now, use the TrueType invalid glyph
330                    * at 0.
331                    */
332                   glyph_indexes[num_valid_glyphs] = 0;
333                 }
334               else
335                 glyph_indexes[num_valid_glyphs] = glyphs->glyphs[i].glyph;
336
337               x_offset += glyphs->glyphs[i].geometry.width;
338
339               /* If the next glyph has an X offset, take that into consideration now */
340               if (i < glyphs->num_glyphs - 1)
341                 next_x_offset = glyphs->glyphs[i+1].geometry.x_offset;
342               else
343                 next_x_offset = 0;
344
345               dX[num_valid_glyphs] = PANGO_PIXELS (x_offset + next_x_offset) - this_x;
346
347               /* Prepare for next glyph */
348               this_x += dX[num_valid_glyphs];
349               num_valid_glyphs++;
350             }
351           i++;
352         }
353 #ifdef PANGO_WIN32_DEBUGGING
354       if (_pango_win32_debug)
355         {
356           g_print ("ExtTextOutW at %d,%d deltas:",
357                    x + PANGO_PIXELS (start_x_offset),
358                    y + PANGO_PIXELS (cur_y_offset));
359           for (j = 0; j < num_valid_glyphs; j++)
360             g_print (" %d", dX[j]);
361           g_print ("\n");
362         }
363 #endif
364
365       ExtTextOutW (hdc,
366                    x + PANGO_PIXELS (start_x_offset),
367                    y + PANGO_PIXELS (cur_y_offset),
368                    ETO_GLYPH_INDEX,
369                    NULL,
370                    glyph_indexes, num_valid_glyphs,
371                    dX);
372       x += this_x;
373     }
374
375
376   SelectObject (hdc, old_hfont); /* restore */
377   g_free (glyph_indexes);
378   g_free (dX);
379 }
380
381 /**
382  * pango_win32_render_transformed:
383  * @hdc:     a windows device context
384  * @matrix:  a #PangoMatrix, or %NULL to use an identity transformation
385  * @font:    the font in which to draw the string
386  * @glyphs:  the glyph string to draw
387  * @x:       the x position of the start of the string (in Pango
388  *           units in user space coordinates)
389  * @y:       the y position of the baseline (in Pango units
390  *           in user space coordinates)
391  *
392  * Renders a #PangoGlyphString onto a windows DC, possibly
393  * transforming the layed-out coordinates through a transformation
394  * matrix. Note that the transformation matrix for @font is not
395  * changed, so to produce correct rendering results, the @font
396  * must have been loaded using a #PangoContext with an identical
397  * transformation matrix to that passed in to this function.
398  **/
399 void
400 pango_win32_render_transformed (HDC                hdc,
401                                 const PangoMatrix *matrix,
402                                 PangoFont         *font,
403                                 PangoGlyphString  *glyphs,
404                                 int                x,
405                                 int                y)
406 {
407   XFORM xForm;
408   XFORM xFormPrev = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
409   int   mode = GetGraphicsMode (hdc);
410
411   if (!SetGraphicsMode (hdc, GM_ADVANCED))
412     g_warning ("SetGraphicsMode() failed");
413   else if (!GetWorldTransform (hdc, &xFormPrev))
414     g_warning ("GetWorldTransform() failed");
415   else if (matrix)
416     {
417       xForm.eM11 = matrix->xx;
418       xForm.eM12 = matrix->yx;
419       xForm.eM21 = matrix->xy;
420       xForm.eM22 = matrix->yy;
421       xForm.eDx = matrix->x0;
422       xForm.eDy = matrix->y0;
423       if (!SetWorldTransform (hdc, &xForm))
424         g_warning ("GetWorldTransform() failed");
425     }
426
427   pango_win32_render (hdc, font, glyphs, x/PANGO_SCALE, y/PANGO_SCALE);
428
429   /* restore */
430   SetWorldTransform (hdc, &xFormPrev);
431   SetGraphicsMode (hdc, mode);
432 }
433
434 static void
435 pango_win32_font_get_glyph_extents (PangoFont      *font,
436                                     PangoGlyph      glyph,
437                                     PangoRectangle *ink_rect,
438                                     PangoRectangle *logical_rect)
439 {
440   PangoWin32Font *win32font = (PangoWin32Font *)font;
441   guint16 glyph_index = glyph;
442   GLYPHMETRICS gm;
443   guint32 res;
444   HFONT hfont;
445   MAT2 m = {{0,1}, {0,0}, {0,0}, {0,1}};
446   PangoWin32GlyphInfo *info;
447
448   if (glyph == PANGO_GLYPH_EMPTY)
449     {
450       if (ink_rect)
451         ink_rect->x = ink_rect->width = ink_rect->y = ink_rect->height = 0;
452       if (logical_rect)
453         logical_rect->x = logical_rect->width = logical_rect->y = logical_rect->height = 0;
454       return;
455     }
456
457   if (glyph & PANGO_GLYPH_UNKNOWN_FLAG)
458     glyph_index = glyph = 0;
459
460   info = g_hash_table_lookup (win32font->glyph_info, GUINT_TO_POINTER (glyph));
461
462   if (!info)
463     {
464       info = g_new0 (PangoWin32GlyphInfo, 1);
465
466       memset (&gm, 0, sizeof (gm));
467
468       hfont = pango_win32_get_hfont (font);
469       SelectObject (_pango_win32_hdc, hfont);
470       /* FIXME: (Alex) This constant reuse of _pango_win32_hdc is
471          not thread-safe */
472       res = GetGlyphOutlineA (_pango_win32_hdc,
473                               glyph_index,
474                               GGO_METRICS | GGO_GLYPH_INDEX,
475                               &gm,
476                               0, NULL,
477                               &m);
478
479       if (res == GDI_ERROR)
480         {
481           gchar *error = g_win32_error_message (GetLastError ());
482           g_warning ("GetGlyphOutline(%04X) failed: %s\n",
483                      glyph_index, error);
484           g_free (error);
485
486           /* Don't just return now, use the still zeroed out gm */
487         }
488
489       info->ink_rect.x = PANGO_SCALE * gm.gmptGlyphOrigin.x;
490       info->ink_rect.width = PANGO_SCALE * gm.gmBlackBoxX;
491       info->ink_rect.y = - PANGO_SCALE * gm.gmptGlyphOrigin.y;
492       info->ink_rect.height = PANGO_SCALE * gm.gmBlackBoxY;
493
494       info->logical_rect.x = 0;
495       info->logical_rect.width = PANGO_SCALE * gm.gmCellIncX;
496       info->logical_rect.y = - PANGO_SCALE * win32font->tm_ascent;
497       info->logical_rect.height = PANGO_SCALE * (win32font->tm_ascent + win32font->tm_descent);
498
499       g_hash_table_insert (win32font->glyph_info, GUINT_TO_POINTER(glyph), info);
500     }
501
502   if (ink_rect)
503     *ink_rect = info->ink_rect;
504
505   if (logical_rect)
506     *logical_rect = info->logical_rect;
507 }
508
509 static int
510 max_glyph_width (PangoLayout *layout)
511 {
512   int max_width = 0;
513   GSList *l, *r;
514
515   for (l = pango_layout_get_lines_readonly (layout); l; l = l->next)
516     {
517       PangoLayoutLine *line = l->data;
518
519       for (r = line->runs; r; r = r->next)
520         {
521           PangoGlyphString *glyphs = ((PangoGlyphItem *)r->data)->glyphs;
522           int i;
523
524           for (i = 0; i < glyphs->num_glyphs; i++)
525             if (glyphs->glyphs[i].geometry.width > max_width)
526               max_width = glyphs->glyphs[i].geometry.width;
527         }
528     }
529
530   return max_width;
531 }
532
533 static PangoFontMetrics *
534 pango_win32_font_get_metrics (PangoFont     *font,
535                               PangoLanguage *language)
536 {
537   PangoWin32MetricsInfo *info = NULL; /* Quiet gcc */
538   PangoWin32Font *win32font = (PangoWin32Font *)font;
539   GSList *tmp_list;
540
541   const char *sample_str = pango_language_get_sample_string (language);
542
543   tmp_list = win32font->metrics_by_lang;
544   while (tmp_list)
545     {
546       info = tmp_list->data;
547
548       if (info->sample_str == sample_str)    /* We _don't_ need strcmp */
549         break;
550
551       tmp_list = tmp_list->next;
552     }
553
554   if (!tmp_list)
555     {
556       HFONT hfont;
557       PangoFontMetrics *metrics;
558
559       info = g_new (PangoWin32MetricsInfo, 1);
560       win32font->metrics_by_lang = g_slist_prepend (win32font->metrics_by_lang, info);
561
562       info->sample_str = sample_str;
563       info->metrics = metrics = pango_font_metrics_new ();
564
565       hfont = pango_win32_get_hfont (font);
566       if (hfont != NULL)
567         {
568           PangoCoverage *coverage;
569           TEXTMETRIC tm;
570
571           SelectObject (_pango_win32_hdc, hfont);
572           GetTextMetrics (_pango_win32_hdc, &tm);
573
574           metrics->ascent = tm.tmAscent * PANGO_SCALE;
575           metrics->descent = tm.tmDescent * PANGO_SCALE;
576           metrics->approximate_char_width = tm.tmAveCharWidth * PANGO_SCALE;
577
578           coverage = pango_win32_font_get_coverage (font, language);
579           if (pango_coverage_get (coverage, '0') != PANGO_COVERAGE_NONE &&
580               pango_coverage_get (coverage, '9') != PANGO_COVERAGE_NONE)
581             {
582               PangoContext *context;
583               PangoFontDescription *font_desc;
584               PangoLayout *layout;
585
586               /*  Get the average width of the chars in "0123456789" */
587               context = pango_font_map_create_context (pango_win32_font_map_for_display ());
588               pango_context_set_language (context, language);
589               font_desc = pango_font_describe_with_absolute_size (font);
590               pango_context_set_font_description (context, font_desc);
591               layout = pango_layout_new (context);
592               pango_layout_set_text (layout, "0123456789", -1);
593
594               metrics->approximate_digit_width = max_glyph_width (layout);
595
596               pango_font_description_free (font_desc);
597               g_object_unref (layout);
598               g_object_unref (context);
599             }
600           else
601             metrics->approximate_digit_width = metrics->approximate_char_width;
602
603           pango_coverage_unref (coverage);
604
605           /* FIXME: Should get the real values from the TrueType font file */
606           metrics->underline_position = -2 * PANGO_SCALE;
607           metrics->underline_thickness = 1 * PANGO_SCALE;
608           metrics->strikethrough_thickness = metrics->underline_thickness;
609           /* Really really wild guess */
610           metrics->strikethrough_position = metrics->ascent / 3;
611         }
612     }
613
614   return pango_font_metrics_ref (info->metrics);
615 }
616
617 static PangoFontMap *
618 pango_win32_font_get_font_map (PangoFont *font)
619 {
620   PangoWin32Font *win32font = (PangoWin32Font *)font;
621
622   return win32font->fontmap;
623 }
624
625 static gboolean
626 pango_win32_font_real_select_font (PangoFont *font,
627                                    HDC        hdc)
628 {
629   HFONT hfont = pango_win32_get_hfont (font);
630
631   if (!hfont)
632     return FALSE;
633
634   if (!SelectObject (hdc, hfont))
635     {
636       g_warning ("pango_win32_font_real_select_font: Cannot select font\n");
637       return FALSE;
638     }
639
640   return TRUE;
641 }
642
643 static void
644 pango_win32_font_real_done_font (PangoFont *font)
645 {
646 }
647
648 static double
649 pango_win32_font_real_get_metrics_factor (PangoFont *font)
650 {
651   return PANGO_SCALE;
652 }
653
654 /**
655  * pango_win32_font_logfont:
656  * @font: a #PangoFont which must be from the Win32 backend
657  *
658  * Determine the LOGFONTA struct for the specified font. Note that
659  * Pango internally uses LOGFONTW structs, so if converting the UTF-16
660  * face name in the LOGFONTW struct to system codepage fails, the
661  * returned LOGFONTA will have an emppty face name. To get the
662  * LOGFONTW of a PangoFont, use pango_win32_font_logfontw(). It
663  * is recommended to do that always even if you don't expect
664  * to come across fonts with odd names.
665  *
666  * Return value: A newly allocated LOGFONTA struct. It must be
667  * freed with g_free().
668  **/
669 LOGFONTA *
670 pango_win32_font_logfont (PangoFont *font)
671 {
672   PangoWin32Font *win32font = (PangoWin32Font *)font;
673   LOGFONTA *lfp;
674
675   g_return_val_if_fail (font != NULL, NULL);
676   g_return_val_if_fail (PANGO_WIN32_IS_FONT (font), NULL);
677
678   lfp = g_new (LOGFONTA, 1);
679
680   *lfp = *(LOGFONTA*) &win32font->logfontw;
681   if (!WideCharToMultiByte (CP_ACP, 0,
682                             win32font->logfontw.lfFaceName, -1,
683                             lfp->lfFaceName, G_N_ELEMENTS (lfp->lfFaceName),
684                             NULL, NULL))
685     lfp->lfFaceName[0] = '\0';
686
687   return lfp;
688 }
689
690 /**
691  * pango_win32_font_logfontw:
692  * @font: a #PangoFont which must be from the Win32 backend
693  * 
694  * Determine the LOGFONTW struct for the specified font.
695  * 
696  * Return value: A newly allocated LOGFONTW struct. It must be
697  * freed with g_free().
698  *
699  * Since: 1.16
700  **/
701 LOGFONTW *
702 pango_win32_font_logfontw (PangoFont *font)
703 {
704   PangoWin32Font *win32font = (PangoWin32Font *)font;
705   LOGFONTW *lfp;
706
707   g_return_val_if_fail (font != NULL, NULL);
708   g_return_val_if_fail (PANGO_WIN32_IS_FONT (font), NULL);
709
710   lfp = g_new (LOGFONTW, 1);
711   *lfp = win32font->logfontw;
712
713   return lfp;
714 }
715
716 /**
717  * pango_win32_font_select_font:
718  * @font: a #PangoFont from the Win32 backend
719  * @hdc: a windows device context
720  *
721  * Selects the font into the specified DC and changes the mapping mode
722  * and world transformation of the DC appropriately for the font.
723  * You may want to surround the use of this function with calls
724  * to SaveDC() and RestoreDC(). Call pango_win32_font_done_font() when
725  * you are done using the DC to release allocated resources.
726  *
727  * See pango_win32_font_get_metrics_factor() for information about
728  * converting from the coordinate space used by this function
729  * into Pango units.
730  *
731  * Return value: %TRUE if the operation succeeded.
732  **/
733 gboolean
734 pango_win32_font_select_font (PangoFont *font,
735                               HDC        hdc)
736 {
737   g_return_val_if_fail (PANGO_WIN32_IS_FONT (font), FALSE);
738
739   return PANGO_WIN32_FONT_GET_CLASS (font)->select_font (font, hdc);
740 }
741
742 /**
743  * pango_win32_font_done_font:
744  * @font: a #PangoFont from the win32 backend
745  *
746  * Releases any resources allocated by pango_win32_font_done_font()
747  **/
748 void
749 pango_win32_font_done_font (PangoFont *font)
750 {
751   g_return_if_fail (PANGO_WIN32_IS_FONT (font));
752
753   PANGO_WIN32_FONT_GET_CLASS (font)->done_font (font);
754 }
755
756 /**
757  * pango_win32_font_get_metrics_factor:
758  * @font: a #PangoFont from the win32 backend
759  *
760  * Returns the scale factor from logical units in the coordinate
761  * space used by pango_win32_font_select_font() to Pango units
762  * in user space.
763  *
764  * Return value: factor to multiply logical units by to get Pango
765  *               units.
766  **/
767 double
768 pango_win32_font_get_metrics_factor (PangoFont *font)
769 {
770   g_return_val_if_fail (PANGO_WIN32_IS_FONT (font), 1.);
771
772   return PANGO_WIN32_FONT_GET_CLASS (font)->get_metrics_factor (font);
773 }
774
775 static void
776 pango_win32_fontmap_cache_add (PangoFontMap   *fontmap,
777                                PangoWin32Font *win32font)
778 {
779   PangoWin32FontMap *win32fontmap = PANGO_WIN32_FONT_MAP (fontmap);
780
781   if (win32fontmap->freed_fonts->length == MAX_FREED_FONTS)
782     {
783       PangoWin32Font *old_font = g_queue_pop_tail (win32fontmap->freed_fonts);
784       g_object_unref (old_font);
785     }
786
787   g_object_ref (win32font);
788   g_queue_push_head (win32fontmap->freed_fonts, win32font);
789   win32font->in_cache = TRUE;
790 }
791
792 static void
793 pango_win32_font_dispose (GObject *object)
794 {
795   PangoWin32Font *win32font = PANGO_WIN32_FONT (object);
796
797   /* If the font is not already in the freed-fonts cache, add it,
798    * if it is already there, do nothing and the font will be
799    * freed.
800    */
801   if (!win32font->in_cache && win32font->fontmap)
802     pango_win32_fontmap_cache_add (win32font->fontmap, win32font);
803
804   G_OBJECT_CLASS (_pango_win32_font_parent_class)->dispose (object);
805 }
806
807 static void
808 free_metrics_info (PangoWin32MetricsInfo *info)
809 {
810   pango_font_metrics_unref (info->metrics);
811   g_free (info);
812 }
813
814 static void
815 pango_win32_font_entry_remove (PangoWin32Face *face,
816                                PangoFont      *font)
817 {
818   face->cached_fonts = g_slist_remove (face->cached_fonts, font);
819 }
820
821 static void
822 pango_win32_font_finalize (GObject *object)
823 {
824   PangoWin32Font *win32font = (PangoWin32Font *)object;
825   PangoWin32FontCache *cache = pango_win32_font_map_get_font_cache (win32font->fontmap);
826
827   if (win32font->hfont != NULL)
828     pango_win32_font_cache_unload (cache, win32font->hfont);
829
830   g_slist_foreach (win32font->metrics_by_lang, (GFunc)free_metrics_info, NULL);
831   g_slist_free (win32font->metrics_by_lang);
832
833   if (win32font->win32face)
834     pango_win32_font_entry_remove (win32font->win32face, PANGO_FONT (win32font));
835
836   g_hash_table_destroy (win32font->glyph_info);
837
838   g_assert (win32font->fontmap != NULL);
839   g_object_remove_weak_pointer (G_OBJECT (win32font->fontmap), (gpointer *) (gpointer) &win32font->fontmap);
840   win32font->fontmap = NULL;
841
842   G_OBJECT_CLASS (_pango_win32_font_parent_class)->finalize (object);
843 }
844
845 static PangoFontDescription *
846 pango_win32_font_describe (PangoFont *font)
847 {
848   PangoFontDescription *desc;
849   PangoWin32Font *win32font = PANGO_WIN32_FONT (font);
850
851   desc = pango_font_description_copy (win32font->win32face->description);
852   pango_font_description_set_size (desc, win32font->size / (PANGO_SCALE / PANGO_WIN32_FONT_MAP (win32font->fontmap)->resolution));
853
854   return desc;
855 }
856
857 static PangoFontDescription *
858 pango_win32_font_describe_absolute (PangoFont *font)
859 {
860   PangoFontDescription *desc;
861   PangoWin32Font *win32font = PANGO_WIN32_FONT (font);
862
863   desc = pango_font_description_copy (win32font->win32face->description);
864   pango_font_description_set_absolute_size (desc, win32font->size);
865
866   return desc;
867 }
868
869 static PangoMap *
870 pango_win32_get_shaper_map (PangoLanguage *lang)
871 {
872   static guint engine_type_id = 0;
873   static guint render_type_id = 0;
874
875   if (engine_type_id == 0)
876     {
877       engine_type_id = g_quark_from_static_string (PANGO_ENGINE_TYPE_SHAPE);
878       render_type_id = g_quark_from_static_string (PANGO_RENDER_TYPE_WIN32);
879     }
880
881   return pango_find_map (lang, engine_type_id, render_type_id);
882 }
883
884 static gint
885 pango_win32_coverage_language_classify (PangoLanguage *lang)
886 {
887   if (pango_language_matches (lang, "zh-tw"))
888     return PANGO_WIN32_COVERAGE_ZH_TW;
889   else if (pango_language_matches (lang, "zh-cn"))
890     return PANGO_WIN32_COVERAGE_ZH_CN;
891   else if (pango_language_matches (lang, "ja"))
892     return PANGO_WIN32_COVERAGE_JA;
893   else if (pango_language_matches (lang, "ko"))
894     return PANGO_WIN32_COVERAGE_KO;
895   else if (pango_language_matches (lang, "vi"))
896     return PANGO_WIN32_COVERAGE_VI;
897   else
898     return PANGO_WIN32_COVERAGE_UNSPEC;
899 }
900
901 static PangoCoverage *
902 pango_win32_font_entry_get_coverage (PangoWin32Face *face,
903                                      PangoLanguage  *lang)
904 {
905   gint i = pango_win32_coverage_language_classify (lang);
906   if (face->coverages[i])
907     {
908       pango_coverage_ref (face->coverages[i]);
909       return face->coverages[i];
910     }
911
912   return NULL;
913 }
914
915 static void
916 pango_win32_font_entry_set_coverage (PangoWin32Face *face,
917                                      PangoCoverage  *coverage,
918                                      PangoLanguage  *lang)
919 {
920   face->coverages[pango_win32_coverage_language_classify (lang)] = pango_coverage_ref (coverage);
921 }
922
923 static PangoCoverage *
924 pango_win32_font_get_coverage (PangoFont     *font,
925                                PangoLanguage *lang)
926 {
927   PangoCoverage *coverage;
928   PangoWin32Font *win32font = (PangoWin32Font *)font;
929
930   coverage = pango_win32_font_entry_get_coverage (win32font->win32face, lang);
931   if (!coverage)
932     {
933       coverage = pango_coverage_new ();
934       pango_win32_font_calc_coverage (font, coverage, lang);
935
936       pango_win32_font_entry_set_coverage (win32font->win32face, coverage, lang);
937     }
938
939   return coverage;
940 }
941
942 static PangoEngineShape *
943 pango_win32_font_find_shaper (PangoFont     *font,
944                               PangoLanguage *lang,
945                               guint32        ch)
946 {
947   PangoMap *shape_map = NULL;
948   PangoScript script;
949
950   shape_map = pango_win32_get_shaper_map (lang);
951   script = pango_script_for_unichar (ch);
952   return (PangoEngineShape *)pango_map_get_engine (shape_map, script);
953 }
954
955 /* Utility functions */
956
957 /**
958  * pango_win32_get_unknown_glyph:
959  * @font: a #PangoFont
960  * @wc: the Unicode character for which a glyph is needed.
961  *
962  * Returns the index of a glyph suitable for drawing @wc as an
963  * unknown character.
964  *
965  * Use PANGO_GET_UNKNOWN_GLYPH() instead.
966  *
967  * Return value: a glyph index into @font
968  **/
969 PangoGlyph
970 pango_win32_get_unknown_glyph (PangoFont *font,
971                                gunichar   wc)
972 {
973   return PANGO_GET_UNKNOWN_GLYPH (wc);
974 }
975
976 /**
977  * pango_win32_render_layout_line:
978  * @hdc:       DC to use for uncolored drawing
979  * @line:      a #PangoLayoutLine
980  * @x:         the x position of start of string (in pixels)
981  * @y:         the y position of baseline (in pixels)
982  *
983  * Render a #PangoLayoutLine onto a device context. For underlining to
984  * work property the text alignment of the DC should have TA_BASELINE
985  * and TA_LEFT.
986  */
987 void
988 pango_win32_render_layout_line (HDC              hdc,
989                                 PangoLayoutLine *line,
990                                 int              x,
991                                 int              y)
992 {
993   GSList *tmp_list = line->runs;
994   PangoRectangle overall_rect;
995   PangoRectangle logical_rect;
996   PangoRectangle ink_rect;
997
998   int x_off = 0;
999
1000   pango_layout_line_get_extents (line,NULL, &overall_rect);
1001
1002   while (tmp_list)
1003     {
1004       HBRUSH oldfg = NULL;
1005       HBRUSH brush = NULL;
1006       POINT points[2];
1007       PangoUnderline uline = PANGO_UNDERLINE_NONE;
1008       PangoLayoutRun *run = tmp_list->data;
1009       PangoAttrColor fg_color, bg_color;
1010       gboolean fg_set, bg_set;
1011
1012       tmp_list = tmp_list->next;
1013
1014       pango_win32_get_item_properties (run->item, &uline, &fg_color, &fg_set, &bg_color, &bg_set);
1015
1016       if (uline == PANGO_UNDERLINE_NONE)
1017         pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
1018                                     NULL, &logical_rect);
1019       else
1020         pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
1021                                     &ink_rect, &logical_rect);
1022
1023       if (bg_set)
1024         {
1025           HBRUSH oldbrush;
1026
1027           brush = CreateSolidBrush (RGB ((bg_color.color.red + 128) >> 8,
1028                                          (bg_color.color.green + 128) >> 8,
1029                                          (bg_color.color.blue + 128) >> 8));
1030           oldbrush = SelectObject (hdc, brush);
1031           Rectangle (hdc, x + PANGO_PIXELS (x_off + logical_rect.x),
1032                           y + PANGO_PIXELS (overall_rect.y),
1033                           PANGO_PIXELS (logical_rect.width),
1034                           PANGO_PIXELS (overall_rect.height));
1035           SelectObject (hdc, oldbrush);
1036           DeleteObject (brush);
1037         }
1038
1039       if (fg_set)
1040         {
1041           brush = CreateSolidBrush (RGB ((fg_color.color.red + 128) >> 8,
1042                                          (fg_color.color.green + 128) >> 8,
1043                                          (fg_color.color.blue + 128) >> 8));
1044           oldfg = SelectObject (hdc, brush);
1045         }
1046
1047       pango_win32_render (hdc, run->item->analysis.font, run->glyphs,
1048                           x + PANGO_PIXELS (x_off), y);
1049
1050       switch (uline)
1051         {
1052         case PANGO_UNDERLINE_NONE:
1053           break;
1054         case PANGO_UNDERLINE_DOUBLE:
1055           points[0].x = x + PANGO_PIXELS (x_off + ink_rect.x) - 1;
1056           points[0].y = points[1].y = y + 4;
1057           points[1].x = x + PANGO_PIXELS (x_off + ink_rect.x + ink_rect.width);
1058           Polyline (hdc, points, 2);
1059           points[0].y = points[1].y = y + 2;
1060           Polyline (hdc, points, 2);
1061           break;
1062         case PANGO_UNDERLINE_SINGLE:
1063           points[0].x = x + PANGO_PIXELS (x_off + ink_rect.x) - 1;
1064           points[0].y = points[1].y = y + 2;
1065           points[1].x = x + PANGO_PIXELS (x_off + ink_rect.x + ink_rect.width);
1066           Polyline (hdc, points, 2);
1067           break;
1068         case PANGO_UNDERLINE_ERROR:
1069           {
1070             int point_x;
1071             int counter = 0;
1072             int end_x = x + PANGO_PIXELS (x_off + ink_rect.x + ink_rect.width);
1073
1074             for (point_x = x + PANGO_PIXELS (x_off + ink_rect.x) - 1;
1075                  point_x <= end_x;
1076                  point_x += 2)
1077             {
1078               points[0].x = point_x;
1079               points[1].x = MAX (point_x + 1, end_x);
1080
1081               if (counter)
1082                 points[0].y = points[1].y = y + 2;
1083               else
1084                 points[0].y = points[1].y = y + 3;
1085
1086               Polyline (hdc, points, 2);
1087               counter = (counter + 1) % 2;
1088             }
1089           }
1090           break;
1091         case PANGO_UNDERLINE_LOW:
1092           points[0].x = x + PANGO_PIXELS (x_off + ink_rect.x) - 1;
1093           points[0].y = points[1].y = y + PANGO_PIXELS (ink_rect.y + ink_rect.height) + 2;
1094           points[1].x = x + PANGO_PIXELS (x_off + ink_rect.x + ink_rect.width);
1095           Polyline (hdc, points, 2);
1096           break;
1097         }
1098
1099       if (fg_set)
1100         {
1101           SelectObject (hdc, oldfg);
1102           DeleteObject (brush);
1103         }
1104
1105       x_off += logical_rect.width;
1106     }
1107 }
1108
1109 /**
1110  * pango_win32_render_layout:
1111  * @hdc:       HDC to use for uncolored drawing
1112  * @layout:    a #PangoLayout
1113  * @x:         the X position of the left of the layout (in pixels)
1114  * @y:         the Y position of the top of the layout (in pixels)
1115  *
1116  * Render a #PangoLayoutLine onto an X drawable
1117  */
1118 void
1119 pango_win32_render_layout (HDC          hdc,
1120                            PangoLayout *layout,
1121                            int          x,
1122                            int          y)
1123 {
1124   PangoLayoutIter *iter;
1125
1126   g_return_if_fail (hdc != NULL);
1127   g_return_if_fail (PANGO_IS_LAYOUT (layout));
1128
1129   iter = pango_layout_get_iter (layout);
1130
1131   do
1132     {
1133       PangoRectangle   logical_rect;
1134       PangoLayoutLine *line;
1135       int              baseline;
1136
1137       line = pango_layout_iter_get_line_readonly (iter);
1138
1139       pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
1140       baseline = pango_layout_iter_get_baseline (iter);
1141
1142       pango_win32_render_layout_line (hdc,
1143                                       line,
1144                                       x + PANGO_PIXELS (logical_rect.x),
1145                                       y + PANGO_PIXELS (baseline));
1146     }
1147   while (pango_layout_iter_next_line (iter));
1148
1149   pango_layout_iter_free (iter);
1150 }
1151
1152 /* This utility function is duplicated here and in pango-layout.c; should it be
1153  * public? Trouble is - what is the appropriate set of properties?
1154  */
1155 static void
1156 pango_win32_get_item_properties (PangoItem      *item,
1157                                  PangoUnderline *uline,
1158                                  PangoAttrColor *fg_color,
1159                                  gboolean       *fg_set,
1160                                  PangoAttrColor *bg_color,
1161                                  gboolean       *bg_set)
1162 {
1163   GSList *tmp_list = item->analysis.extra_attrs;
1164
1165   if (fg_set)
1166     *fg_set = FALSE;
1167
1168   if (bg_set)
1169     *bg_set = FALSE;
1170
1171   while (tmp_list)
1172     {
1173       PangoAttribute *attr = tmp_list->data;
1174
1175       switch (attr->klass->type)
1176         {
1177         case PANGO_ATTR_UNDERLINE:
1178           if (uline)
1179             *uline = ((PangoAttrInt *)attr)->value;
1180           break;
1181
1182         case PANGO_ATTR_FOREGROUND:
1183           if (fg_color)
1184             *fg_color = *((PangoAttrColor *)attr);
1185           if (fg_set)
1186             *fg_set = TRUE;
1187
1188           break;
1189
1190         case PANGO_ATTR_BACKGROUND:
1191           if (bg_color)
1192             *bg_color = *((PangoAttrColor *)attr);
1193           if (bg_set)
1194             *bg_set = TRUE;
1195
1196           break;
1197
1198         default:
1199           break;
1200         }
1201       tmp_list = tmp_list->next;
1202     }
1203 }
1204
1205 static guint
1206 get_cmap_offset (HDC     hdc,
1207                  guint16 encoding_id)
1208 {
1209   guint16 n_tables;
1210   struct cmap_encoding_subtable *table;
1211   gint32 res;
1212   int i;
1213   guint32 offset;
1214
1215   /* Get The number of encoding tables, at offset 2 */
1216   res = GetFontData (hdc, CMAP, 2, &n_tables, 2);
1217   if (res != 2)
1218     return 0;
1219
1220   n_tables = GUINT16_FROM_BE (n_tables);
1221
1222   table = g_malloc (ENCODING_TABLE_SIZE*n_tables);
1223
1224   res = GetFontData (hdc, CMAP, CMAP_HEADER_SIZE, table, ENCODING_TABLE_SIZE*n_tables);
1225   if (res != ENCODING_TABLE_SIZE*n_tables)
1226     return 0;
1227
1228   for (i = 0; i < n_tables; i++)
1229     {
1230       if (table[i].platform_id == GUINT16_TO_BE (MICROSOFT_PLATFORM_ID) &&
1231           table[i].encoding_id == GUINT16_TO_BE (encoding_id))
1232         {
1233           offset = GUINT32_FROM_BE (table[i].offset);
1234           g_free (table);
1235           return offset;
1236         }
1237     }
1238   g_free (table);
1239   return 0;
1240 }
1241
1242 static gpointer
1243 get_format_4_cmap (HDC hdc)
1244 {
1245   guint32 offset;
1246   guint32 res;
1247   guint16 length;
1248   guint16 *tbl, *tbl_end;
1249   struct format_4_cmap *table;
1250
1251   /* FIXME: Could look here at the CRC for the font in the DC
1252             and return a cached copy if the same */
1253
1254   offset = get_cmap_offset (hdc, UNICODE_ENCODING_ID);
1255   if (offset == 0)
1256     return NULL;
1257
1258   res = GetFontData (hdc, CMAP, offset + 2, &length, 2);
1259   if (res != 2)
1260     return NULL;
1261   length = GUINT16_FROM_BE (length);
1262
1263   table = g_malloc (length);
1264
1265   res = GetFontData (hdc, CMAP, offset, table, length);
1266   if (res != length ||
1267       GUINT16_FROM_BE (table->format) != 4 ||
1268       (GUINT16_FROM_BE (table->length) % 2) != 0)
1269     {
1270       g_free (table);
1271       return NULL;
1272     }
1273
1274   table->format = GUINT16_FROM_BE (table->format);
1275   table->length = GUINT16_FROM_BE (table->length);
1276   table->language = GUINT16_FROM_BE (table->language);
1277   table->seg_count_x_2 = GUINT16_FROM_BE (table->seg_count_x_2);
1278   table->search_range = GUINT16_FROM_BE (table->search_range);
1279   table->entry_selector = GUINT16_FROM_BE (table->entry_selector);
1280   table->range_shift = GUINT16_FROM_BE (table->range_shift);
1281
1282   tbl_end = (guint16 *)((char *)table + length);
1283   tbl = &table->reserved;
1284
1285   while (tbl < tbl_end)
1286     {
1287       *tbl = GUINT16_FROM_BE (*tbl);
1288       tbl++;
1289     }
1290
1291   return table;
1292 }
1293
1294 static guint16 *
1295 get_id_range_offset (struct format_4_cmap *table)
1296 {
1297   gint32 seg_count = table->seg_count_x_2/2;
1298   return &table->arrays[seg_count*3];
1299 }
1300
1301 static guint16 *
1302 get_id_delta (struct format_4_cmap *table)
1303 {
1304   gint32 seg_count = table->seg_count_x_2/2;
1305   return &table->arrays[seg_count*2];
1306 }
1307
1308 static guint16 *
1309 get_start_count (struct format_4_cmap *table)
1310 {
1311   gint32 seg_count = table->seg_count_x_2/2;
1312   return &table->arrays[seg_count*1];
1313 }
1314
1315 static guint16 *
1316 get_end_count (struct format_4_cmap *table)
1317 {
1318   gint32 seg_count = table->seg_count_x_2/2;
1319   /* Apparently the reseved spot is not reserved for
1320      the end_count array!? */
1321   return (&table->arrays[seg_count*0])-1;
1322 }
1323
1324 static gboolean
1325 find_segment (struct format_4_cmap *table,
1326               guint16               wc,
1327               guint16              *segment)
1328 {
1329   guint16 start, end, i;
1330   guint16 seg_count = table->seg_count_x_2/2;
1331   guint16 *end_count = get_end_count (table);
1332   guint16 *start_count = get_start_count (table);
1333   static guint last = 0; /* Cache of one */
1334
1335   if (last < seg_count &&
1336       wc >= start_count[last] &&
1337       wc <= end_count[last])
1338     {
1339       *segment = last;
1340       return TRUE;
1341     }
1342
1343
1344   /* Binary search for the segment */
1345   start = 0; /* inclusive */
1346   end = seg_count; /* not inclusive */
1347   while (start < end)
1348     {
1349       /* Look at middle pos */
1350       i = (start + end)/2;
1351
1352       if (i == start)
1353         {
1354           /* We made no progress. Look if this is the one. */
1355
1356           if (wc >= start_count[i] &&
1357               wc <= end_count[i])
1358             {
1359               *segment = i;
1360               last = i;
1361               return TRUE;
1362             }
1363           else
1364             return FALSE;
1365         }
1366       else if (wc < start_count[i])
1367         {
1368           end = i;
1369         }
1370       else if (wc > end_count[i])
1371         {
1372           start = i;
1373         }
1374       else
1375         {
1376           /* Found it! */
1377           *segment = i;
1378           last = i;
1379           return TRUE;
1380         }
1381     }
1382   return FALSE;
1383 }
1384
1385 static gpointer
1386 get_format_12_cmap (HDC hdc)
1387 {
1388   guint32 offset;
1389   guint32 res;
1390   guint32 length;
1391   guint32 *tbl, *tbl_end;
1392   struct format_12_cmap *table;
1393
1394   offset = get_cmap_offset (hdc, UCS4_ENCODING_ID);
1395   if (offset == 0)
1396     return NULL;
1397
1398   res = GetFontData (hdc, CMAP, offset + 4, &length, 4);
1399   if (res != 4)
1400     return NULL;
1401   length = GUINT32_FROM_BE (length);
1402
1403   table = g_malloc (length);
1404
1405   res = GetFontData (hdc, CMAP, offset, table, length);
1406   if (res != length)
1407     {
1408       g_free (table);
1409       return NULL;
1410     }
1411
1412   table->format = GUINT16_FROM_BE (table->format);
1413   table->length = GUINT32_FROM_BE (table->length);
1414   table->language = GUINT32_FROM_BE (table->language);
1415   table->count = GUINT32_FROM_BE (table->count);
1416
1417   if (table->format != 12 ||
1418       (table->length % 4) != 0 ||
1419       table->length > length ||
1420       table->length < 16 + table->count * 12)
1421     {
1422       g_free (table);
1423       return NULL;
1424     }
1425
1426   tbl_end = (guint32 *) ((char *) table + length);
1427   tbl = table->groups;
1428
1429   while (tbl < tbl_end)
1430     {
1431       *tbl = GUINT32_FROM_BE (*tbl);
1432       tbl++;
1433     }
1434
1435   return table;
1436 }
1437
1438 static gpointer
1439 font_get_cmap (PangoFont *font)
1440 {
1441   PangoWin32Font *win32font = (PangoWin32Font *)font;
1442   gpointer cmap;
1443
1444   if (win32font->win32face->cmap)
1445     return win32font->win32face->cmap;
1446
1447   pango_win32_font_select_font (font, _pango_win32_hdc);
1448
1449   /* Prefer the format 12 cmap */
1450   if ((cmap = get_format_12_cmap (_pango_win32_hdc)) != NULL)
1451     {
1452       win32font->win32face->cmap_format = 12;
1453       win32font->win32face->cmap = cmap;
1454     }
1455   else if ((cmap = get_format_4_cmap (_pango_win32_hdc)) != NULL)
1456     {
1457       win32font->win32face->cmap_format = 4;
1458       win32font->win32face->cmap = cmap;
1459     }
1460
1461   pango_win32_font_done_font (font);
1462
1463   return cmap;
1464 }
1465
1466 static gint
1467 pango_win32_font_get_type1_glyph_index (PangoFont *font,
1468                                         gunichar   wc)
1469 {
1470   wchar_t unicode[2];
1471   WORD glyph_index;
1472   gint32 res;
1473
1474   unicode[0] = wc;
1475   unicode[1] = 0;
1476   pango_win32_font_select_font (font, _pango_win32_hdc);
1477   res = GetGlyphIndicesW (_pango_win32_hdc, unicode, 1, &glyph_index, 0);
1478   pango_win32_font_done_font (font);
1479
1480   if (res == 1)
1481       return glyph_index;
1482   else
1483       return 0;
1484 }
1485
1486 /**
1487  * pango_win32_font_get_glyph_index:
1488  * @font: a #PangoFont.
1489  * @wc: a Unicode character.
1490  *
1491  * Obtains the index of the glyph for @wc in @font, or 0, if not
1492  * covered.
1493  *
1494  * Return value: the glyph index for @wc.
1495  **/
1496 gint
1497 pango_win32_font_get_glyph_index (PangoFont *font,
1498                                   gunichar   wc)
1499 {
1500   PangoWin32Font *win32font = (PangoWin32Font *)font;
1501   gpointer cmap;
1502   guint16 glyph;
1503
1504   if (win32font->win32face->has_cmap)
1505     {
1506       /* Do GetFontData magic on font->hfont here. */
1507       cmap = font_get_cmap (font);
1508       if (cmap == NULL)
1509         win32font->win32face->has_cmap = FALSE;
1510     }
1511
1512   if (!win32font->win32face->has_cmap)
1513     return pango_win32_font_get_type1_glyph_index (font, wc);
1514
1515   if (win32font->win32face->cmap_format == 4)
1516     {
1517       struct format_4_cmap *cmap4 = cmap;
1518       guint16 *id_range_offset;
1519       guint16 *id_delta;
1520       guint16 *start_count;
1521       guint16 segment;
1522       guint16 id;
1523       guint16 ch = wc;
1524
1525       if (wc > 0xFFFF)
1526         return 0;
1527
1528       if (!find_segment (cmap4, ch, &segment))
1529         return 0;
1530
1531       id_range_offset = get_id_range_offset (cmap4);
1532       id_delta = get_id_delta (cmap4);
1533       start_count = get_start_count (cmap4);
1534
1535       if (id_range_offset[segment] == 0)
1536         glyph = (id_delta[segment] + ch) % 65536;
1537       else
1538         {
1539           id = *(id_range_offset[segment]/2 +
1540                  (ch - start_count[segment]) +
1541                  &id_range_offset[segment]);
1542           if (id)
1543             glyph = (id_delta[segment] + id) %65536;
1544           else
1545             glyph = 0;
1546         }
1547     }
1548   else if (win32font->win32face->cmap_format == 12)
1549     {
1550       struct format_12_cmap *cmap12 = cmap;
1551       guint32 i;
1552
1553       glyph = 0;
1554       for (i = 0; i < cmap12->count; i++)
1555         {
1556           if (cmap12->groups[i*3+0] <= wc && wc <= cmap12->groups[i*3+1])
1557             {
1558               glyph = cmap12->groups[i*3+2] + (wc - cmap12->groups[i*3+0]);
1559               break;
1560             }
1561         }
1562     }
1563   else
1564     g_assert_not_reached ();
1565
1566   return glyph;
1567 }
1568
1569 gboolean
1570 _pango_win32_get_name_header (HDC                 hdc,
1571                               struct name_header *header)
1572 {
1573   if (GetFontData (hdc, NAME, 0, header, sizeof (*header)) != sizeof (*header))
1574     return FALSE;
1575
1576   header->num_records = GUINT16_FROM_BE (header->num_records);
1577   header->string_storage_offset = GUINT16_FROM_BE (header->string_storage_offset);
1578
1579   return TRUE;
1580 }
1581
1582 gboolean
1583 _pango_win32_get_name_record (HDC                 hdc,
1584                               gint                i,
1585                               struct name_record *record)
1586 {
1587   if (GetFontData (hdc, NAME, 6 + i * sizeof (*record),
1588                    record, sizeof (*record)) != sizeof (*record))
1589     return FALSE;
1590
1591   record->platform_id = GUINT16_FROM_BE (record->platform_id);
1592   record->encoding_id = GUINT16_FROM_BE (record->encoding_id);
1593   record->language_id = GUINT16_FROM_BE (record->language_id);
1594   record->name_id = GUINT16_FROM_BE (record->name_id);
1595   record->string_length = GUINT16_FROM_BE (record->string_length);
1596   record->string_offset = GUINT16_FROM_BE (record->string_offset);
1597
1598   return TRUE;
1599 }
1600
1601 static gboolean
1602 font_has_name_in (PangoFont                       *font,
1603                   PangoWin32CoverageLanguageClass  cjkv)
1604 {
1605   HFONT hfont, oldhfont;
1606   struct name_header header;
1607   struct name_record record;
1608   gint i;
1609   gboolean retval = FALSE;
1610
1611   if (cjkv == PANGO_WIN32_COVERAGE_UNSPEC)
1612     return TRUE;
1613
1614   hfont = pango_win32_get_hfont (font);
1615   oldhfont = SelectObject (_pango_win32_hdc, hfont);
1616
1617   if (!_pango_win32_get_name_header (_pango_win32_hdc, &header))
1618     {
1619       SelectObject (_pango_win32_hdc, oldhfont);
1620       return FALSE;
1621     }
1622
1623   for (i = 0; i < header.num_records; i++)
1624     {
1625       if (!_pango_win32_get_name_record (_pango_win32_hdc, i, &record))
1626         {
1627           SelectObject (_pango_win32_hdc, oldhfont);
1628           return FALSE;
1629         }
1630
1631       if ((record.name_id != 1 && record.name_id != 16) || record.string_length <= 0)
1632         continue;
1633
1634       PING (("platform:%d encoding:%d language:%04x name_id:%d",
1635              record.platform_id, record.encoding_id, record.language_id, record.name_id));
1636
1637       if (record.platform_id == MICROSOFT_PLATFORM_ID)
1638         if ((cjkv == PANGO_WIN32_COVERAGE_ZH_TW &&
1639              record.language_id == MAKELANGID (LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL))
1640             ||
1641             (cjkv == PANGO_WIN32_COVERAGE_ZH_CN &&
1642              record.language_id == MAKELANGID (LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED))
1643             ||
1644             (cjkv == PANGO_WIN32_COVERAGE_JA &&
1645              PRIMARYLANGID (record.language_id) == LANG_JAPANESE)
1646             ||
1647             (cjkv == PANGO_WIN32_COVERAGE_KO &&
1648              PRIMARYLANGID (record.language_id) == LANG_KOREAN)
1649             ||
1650             (cjkv == PANGO_WIN32_COVERAGE_VI &&
1651              PRIMARYLANGID (record.language_id) == LANG_VIETNAMESE))
1652           {
1653             PING (("yep:%d:%04x", cjkv, record.language_id));
1654             retval = TRUE;
1655             break;
1656           }
1657     }
1658
1659   SelectObject (_pango_win32_hdc, oldhfont);
1660   return retval;
1661 }
1662
1663 static void
1664 pango_win32_font_calc_type1_coverage (PangoFont     *font,
1665                                       PangoCoverage *coverage,
1666                                       PangoLanguage *lang)
1667 {
1668   GLYPHSET *glyph_set;
1669   gint32 res;
1670   guint32 ch;
1671   guint32 i;
1672   
1673   pango_win32_font_select_font (font, _pango_win32_hdc);
1674   res = GetFontUnicodeRanges(_pango_win32_hdc, NULL);
1675   if (res == 0)
1676     goto fail1;
1677
1678   glyph_set = g_malloc (res);
1679   res = GetFontUnicodeRanges(_pango_win32_hdc, glyph_set);
1680   if (res == 0)
1681     goto fail2;
1682   
1683   for (i = 0; i < glyph_set->cRanges; i++) 
1684     {
1685       guint32 end = glyph_set->ranges[i].wcLow + glyph_set->ranges[i].cGlyphs;
1686
1687       for (ch = glyph_set->ranges[i].wcLow; ch < end; ch++)
1688           if (CH_IS_UNIHAN_BMP (ch))
1689               pango_coverage_set (coverage, ch, PANGO_COVERAGE_APPROXIMATE);
1690           else
1691               pango_coverage_set (coverage, ch, PANGO_COVERAGE_EXACT);
1692     }
1693
1694  fail2:
1695   g_free (glyph_set);
1696
1697  fail1:
1698   pango_win32_font_done_font (font);
1699 }
1700
1701 static void
1702 pango_win32_font_calc_coverage (PangoFont     *font,
1703                                 PangoCoverage *coverage,
1704                                 PangoLanguage *lang)
1705 {
1706   PangoWin32Font *win32font = (PangoWin32Font *)font;
1707   gpointer cmap;
1708   guint32 ch;
1709   guint32 i;
1710   PangoWin32CoverageLanguageClass cjkv;
1711   gboolean hide_unihan = FALSE;
1712   PangoFontDescription *desc;
1713   gchar *name;
1714
1715   desc = pango_font_describe (font);
1716   name = pango_font_description_to_string (desc);
1717   PING (("font:%s lang:%s", name,
1718          pango_language_to_string (lang)));
1719   g_free (name);
1720   pango_font_description_free (desc);
1721
1722   if (win32font->win32face->has_cmap)
1723     {
1724       /* Do GetFontData magic on font->hfont here. */
1725       cmap = font_get_cmap (font);
1726       if (cmap == NULL)
1727         win32font->win32face->has_cmap = FALSE;
1728     }
1729
1730   if (!win32font->win32face->has_cmap)
1731     {
1732       pango_win32_font_calc_type1_coverage (font, coverage, lang);
1733       return;
1734     }
1735
1736   cjkv = pango_win32_coverage_language_classify (lang);
1737
1738   if (cjkv != PANGO_WIN32_COVERAGE_UNSPEC && !font_has_name_in (font, cjkv))
1739     {
1740       PING (("hiding UniHan chars"));
1741       hide_unihan = TRUE;
1742     }
1743
1744   PING (("coverage:"));
1745   if (win32font->win32face->cmap_format == 4)
1746     {
1747       struct format_4_cmap *cmap4 = cmap;
1748       guint16 *id_range_offset;
1749       guint16 *start_count;
1750       guint16 *end_count;
1751       guint16 seg_count;
1752       guint16 id;
1753
1754       seg_count = cmap4->seg_count_x_2/2;
1755       end_count = get_end_count (cmap4);
1756       start_count = get_start_count (cmap4);
1757       id_range_offset = get_id_range_offset (cmap4);
1758
1759       for (i = 0; i < seg_count; i++)
1760         {
1761           if (id_range_offset[i] == 0)
1762             {
1763 #ifdef PANGO_WIN32_DEBUGGING
1764               if (_pango_win32_debug)
1765                 {
1766                   if (end_count[i] == start_count[i])
1767                     g_print ("%04x ", start_count[i]);
1768                   else
1769                     g_print ("%04x:%04x ", start_count[i], end_count[i]);
1770                 }
1771 #endif
1772               for (ch = start_count[i]; ch <= end_count[i]; ch++)
1773                 if (hide_unihan && CH_IS_UNIHAN_BMP (ch))
1774                   pango_coverage_set (coverage, ch, PANGO_COVERAGE_APPROXIMATE);
1775                 else
1776                   pango_coverage_set (coverage, ch, PANGO_COVERAGE_EXACT);
1777             }
1778           else
1779             {
1780 #ifdef PANGO_WIN32_DEBUGGING
1781               guint32 ch0 = G_MAXUINT;
1782 #endif
1783               for (ch = start_count[i]; ch <= end_count[i]; ch++)
1784                 {
1785                   if (ch == 0xFFFF)
1786                     break;
1787
1788                   id = *(id_range_offset[i]/2 +
1789                          (ch - start_count[i]) +
1790                          &id_range_offset[i]);
1791                   if (id)
1792                     {
1793 #ifdef PANGO_WIN32_DEBUGGING
1794                       if (ch0 == G_MAXUINT)
1795                         ch0 = ch;
1796 #endif
1797                       if (hide_unihan && CH_IS_UNIHAN_BMP (ch))
1798                         pango_coverage_set (coverage, ch, PANGO_COVERAGE_APPROXIMATE);
1799                       else
1800                         pango_coverage_set (coverage, ch, PANGO_COVERAGE_EXACT);
1801                     }
1802 #ifdef PANGO_WIN32_DEBUGGING
1803                   else if (ch0 < G_MAXUINT)
1804                     {
1805                       if (_pango_win32_debug)
1806                         {
1807                           if (ch > ch0 + 2)
1808                             g_print ("%04x:%04x ", ch0, ch - 1);
1809                           else
1810                             g_print ("%04x ", ch0);
1811                         }
1812                       ch0 = G_MAXUINT;
1813                     }
1814 #endif
1815                 }
1816 #ifdef PANGO_WIN32_DEBUGGING
1817               if (ch0 < G_MAXUINT)
1818                 {
1819                   if (_pango_win32_debug)
1820                     {
1821                       if (ch > ch0 + 2)
1822                         g_print ("%04x:%04x ", ch0, ch - 1);
1823                       else
1824                         g_print ("%04x ", ch0);
1825                     }
1826                 }
1827 #endif
1828             }
1829         }
1830     }
1831   else if (win32font->win32face->cmap_format == 12)
1832     {
1833       struct format_12_cmap *cmap12 = cmap;
1834
1835       for (i = 0; i < cmap12->count; i++)
1836         {
1837 #ifdef PANGO_WIN32_DEBUGGING
1838           if (_pango_win32_debug)
1839             {
1840               if (cmap12->groups[i*3+0] == cmap12->groups[i*3+1])
1841                 g_print ("%04x ", cmap12->groups[i*3+0]);
1842               else
1843                 g_print ("%04x:%04x ", cmap12->groups[i*3+0], cmap12->groups[i*3+1]);
1844             }
1845 #endif
1846           for (ch = cmap12->groups[i*3+0]; ch <= cmap12->groups[i*3+1]; ch++)
1847             {
1848               if (hide_unihan && CH_IS_UNIHAN (ch))
1849                 pango_coverage_set (coverage, ch, PANGO_COVERAGE_APPROXIMATE);
1850               else
1851                 pango_coverage_set (coverage, ch, PANGO_COVERAGE_EXACT);
1852             }
1853         }
1854     }
1855   else
1856     g_assert_not_reached ();
1857 #ifdef PANGO_WIN32_DEBUGGING
1858   if (_pango_win32_debug)
1859     g_print ("\n");
1860 #endif
1861 }