]> code.delx.au - gnu-emacs/blobdiff - src/w32uniscribe.c
Fix bug ref
[gnu-emacs] / src / w32uniscribe.c
index 320862167ec5d607ccfbe677eca69763fd0f5912..5d160b9d42f22dad437e8010a485c4d37370784c 100644 (file)
@@ -1,12 +1,12 @@
 /* Font backend for the Microsoft W32 Uniscribe API.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008-2012 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
-GNU Emacs is free software; you can redistribute it and/or modify
+GNU Emacs is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3, or (at your option)
-any later version.
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -14,18 +14,15 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA.  */
+along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
-#ifdef USE_FONT_BACKEND
 
 #include <config.h>
 /* Override API version - Uniscribe is only available as standard since
    Windows 2000, though most users of older systems will have it
    since it installs with Internet Explorer 5.0 and other software.
    We only enable the feature if it is available, so there is no chance
-   of calling non-existant functions.  */
+   of calling non-existent functions.  */
 #undef _WIN32_WINNT
 #define _WIN32_WINNT 0x500
 #include <windows.h>
@@ -37,6 +34,7 @@ Boston, MA 02110-1301, USA.  */
 #include "dispextern.h"
 #include "character.h"
 #include "charset.h"
+#include "composite.h"
 #include "fontset.h"
 #include "font.h"
 #include "w32font.h"
@@ -53,20 +51,15 @@ int uniscribe_available = 0;
 extern Lisp_Object Quniscribe;
 extern Lisp_Object Qopentype;
 
-extern int initialized;
-
-extern struct font_driver uniscribe_font_driver;
-
 /* EnumFontFamiliesEx callback.  */
-static int CALLBACK add_opentype_font_name_to_list P_ ((ENUMLOGFONTEX *,
-                                                        NEWTEXTMETRICEX *,
-                                                        DWORD, LPARAM));
+static int CALLBACK add_opentype_font_name_to_list (ENUMLOGFONTEX *,
+                                                    NEWTEXTMETRICEX *,
+                                                    DWORD, LPARAM);
 /* Used by uniscribe_otf_capability.  */
 static Lisp_Object otf_features (HDC context, char *table);
 
 static int
-memq_no_quit (elt, list)
-     Lisp_Object elt, list;
+memq_no_quit (Lisp_Object elt, Lisp_Object list)
 {
   while (CONSP (list) && ! EQ (XCAR (list), elt))
     list = XCDR (list);
@@ -76,29 +69,30 @@ memq_no_quit (elt, list)
 \f
 /* Font backend interface implementation.  */
 static Lisp_Object
-uniscribe_list (frame, font_spec)
-     Lisp_Object frame, font_spec;
+uniscribe_list (Lisp_Object frame, Lisp_Object font_spec)
 {
-  return w32font_list_internal (frame, font_spec, 1);
+  Lisp_Object fonts = w32font_list_internal (frame, font_spec, 1);
+  FONT_ADD_LOG ("uniscribe-list", font_spec, fonts);
+  return fonts;
 }
 
 static Lisp_Object
-uniscribe_match (frame, font_spec)
-     Lisp_Object frame, font_spec;
+uniscribe_match (Lisp_Object frame, Lisp_Object font_spec)
 {
-  return w32font_match_internal (frame, font_spec, 1);
+  Lisp_Object entity = w32font_match_internal (frame, font_spec, 1);
+  FONT_ADD_LOG ("uniscribe-match", font_spec, entity);
+  return entity;
 }
 
 static Lisp_Object
-uniscribe_list_family (frame)
-     Lisp_Object frame;
+uniscribe_list_family (Lisp_Object frame)
 {
   Lisp_Object list = Qnil;
   LOGFONT font_match_pattern;
   HDC dc;
   FRAME_PTR f = XFRAME (frame);
 
-  bzero (&font_match_pattern, sizeof (font_match_pattern));
+  memset (&font_match_pattern, 0, sizeof (font_match_pattern));
   /* Limit enumerated fonts to outline fonts to save time.  */
   font_match_pattern.lfOutPrecision = OUT_OUTLINE_PRECIS;
 
@@ -112,44 +106,43 @@ uniscribe_list_family (frame)
   return list;
 }
 
-static struct font *
-uniscribe_open (f, font_entity, pixel_size)
-     FRAME_PTR f;
-     Lisp_Object font_entity;
-     int pixel_size;
+static Lisp_Object
+uniscribe_open (FRAME_PTR f, Lisp_Object font_entity, int pixel_size)
 {
+  Lisp_Object font_object
+    = font_make_object (VECSIZE (struct uniscribe_font_info),
+                       font_entity, pixel_size);
   struct uniscribe_font_info *uniscribe_font
-    = xmalloc (sizeof (struct uniscribe_font_info));
+    = (struct uniscribe_font_info *) XFONT_OBJECT (font_object);
 
-  if (uniscribe_font == NULL)
-    return NULL;
+  ASET (font_object, FONT_TYPE_INDEX, Quniscribe);
 
-  if (!w32font_open_internal (f, font_entity, pixel_size,
-                              (struct w32font_info *) uniscribe_font))
+  if (!w32font_open_internal (f, font_entity, pixel_size, font_object))
     {
-      xfree (uniscribe_font);
-      return NULL;
+      return Qnil;
     }
 
   /* Initialize the cache for this font.  */
   uniscribe_font->cache = NULL;
+
+  /* Uniscribe backend uses glyph indices.  */
+  uniscribe_font->w32_font.glyph_idx = ETO_GLYPH_INDEX;
+
   /* Mark the format as opentype  */
-  uniscribe_font->w32_font.font.format = Qopentype;
+  uniscribe_font->w32_font.font.props[FONT_FORMAT_INDEX] = Qopentype;
   uniscribe_font->w32_font.font.driver = &uniscribe_font_driver;
 
-  return (struct font *) uniscribe_font;
+  return font_object;
 }
 
 static void
-uniscribe_close (f, font)
-     FRAME_PTR f;
-     struct font *font;
+uniscribe_close (FRAME_PTR f, struct font *font)
 {
   struct uniscribe_font_info *uniscribe_font
     = (struct uniscribe_font_info *) font;
 
   if (uniscribe_font->cache)
-    ScriptFreeCache (&uniscribe_font->cache);
+    ScriptFreeCache (&(uniscribe_font->cache));
 
   w32font_close (f, font);
 }
@@ -157,8 +150,7 @@ uniscribe_close (f, font)
 /* Return a list describing which scripts/languages FONT supports by
    which GSUB/GPOS features of OpenType tables.  */
 static Lisp_Object
-uniscribe_otf_capability (font)
-     struct font *font;
+uniscribe_otf_capability (struct font *font)
 {
   HDC context;
   HFONT old_font;
@@ -168,8 +160,7 @@ uniscribe_otf_capability (font)
 
   f = XFRAME (selected_frame);
   context = get_frame_dc (f);
-  old_font = SelectObject (context,
-                          ((W32FontStruct *) (font->font.font))->hfont);
+  old_font = SelectObject (context, FONT_HANDLE (font));
 
   features = otf_features (context, "GSUB");
   XSETCAR (capability, features);
@@ -184,46 +175,47 @@ uniscribe_otf_capability (font)
 
 /* Uniscribe implementation of shape for font backend.
 
-   Shape text in LGSTRING.  See the docstring of `font-make-gstring'
-   for the format of LGSTRING.  If the (N+1)th element of LGSTRING
-   is nil, input of shaping is from the 1st to (N)th elements.  In
-   each input glyph, FROM, TO, CHAR, and CODE are already set.
+   Shape text in LGSTRING.  See the docstring of
+   `composition-get-gstring' for the format of LGSTRING.  If the
+   (N+1)th element of LGSTRING is nil, input of shaping is from the
+   1st to (N)th elements.  In each input glyph, FROM, TO, CHAR, and
+   CODE are already set.
 
    This function updates all fields of the input glyphs.  If the
    output glyphs (M) are more than the input glyphs (N), (N+1)th
    through (M)th elements of LGSTRING are updated possibly by making
    a new glyph object and storing it in LGSTRING.  If (M) is greater
-   than the length of LGSTRING, nil should be return.  In that case,
-   this function is called again with the larger LGSTRING.  */
+   than the length of LGSTRING, nil should be returned.  In that case,
+   this function is called again with a larger LGSTRING.  */
 static Lisp_Object
-uniscribe_shape (lgstring)
-     Lisp_Object lgstring;
+uniscribe_shape (Lisp_Object lgstring)
 {
   struct font * font;
   struct uniscribe_font_info * uniscribe_font;
   EMACS_UINT nchars;
-  int nitems, max_items, i, max_glyphs, done_glyphs, done_chars;
+  int nitems, max_items, i, max_glyphs, done_glyphs;
   wchar_t *chars;
   WORD *glyphs, *clusters;
   SCRIPT_ITEM *items;
-  SCRIPT_CONTROL control;
   SCRIPT_VISATTR *attributes;
   int *advances;
   GOFFSET *offsets;
   ABC overall_metrics;
-  MAT2 transform;
-  HDC context;
-  HFONT old_font;
   HRESULT result;
-  struct frame * f;
+  struct frame * f = NULL;
+  HDC context = NULL;
+  HFONT old_font = NULL;
 
   CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring), font);
   uniscribe_font = (struct uniscribe_font_info *) font;
 
   /* Get the chars from lgstring in a form we can use with uniscribe.  */
-  max_glyphs = nchars = LGSTRING_LENGTH (lgstring);
-  done_glyphs = done_chars = 0;
+  max_glyphs = nchars = LGSTRING_GLYPH_LEN (lgstring);
+  done_glyphs = 0;
   chars = (wchar_t *) alloca (nchars * sizeof (wchar_t));
+  /* FIXME: This loop assumes that characters in the input LGSTRING
+     are all inside the BMP.  Need to encode characters beyond the BMP
+     as UTF-16.  */
   for (i = 0; i < nchars; i++)
     {
       /* lgstring can be bigger than the number of characters in it, in
@@ -238,216 +230,363 @@ uniscribe_shape (lgstring)
   /* First we need to break up the glyph string into runs of glyphs that
      can be treated together.  First try a single run.  */
   max_items = 2;
-  items = (SCRIPT_ITEM *) xmalloc (sizeof (SCRIPT_ITEM) * max_items);
-  bzero (&control, sizeof (control));
+  items = xmalloc (sizeof (SCRIPT_ITEM) * max_items + 1);
 
-  while ((result = ScriptItemize (chars, nchars, max_items, &control, NULL,
+  while ((result = ScriptItemize (chars, nchars, max_items, NULL, NULL,
                                  items, &nitems)) == E_OUTOFMEMORY)
     {
       /* If that wasn't enough, keep trying with one more run.  */
       max_items++;
       items = (SCRIPT_ITEM *) xrealloc (items,
-                                       sizeof (SCRIPT_ITEM) * max_items);
+                                       sizeof (SCRIPT_ITEM) * max_items + 1);
     }
 
-  /* 0 = success in Microsoft's backwards world.  */
-  if (result)
+  if (FAILED (result))
     {
       xfree (items);
       return Qnil;
     }
 
-  f = XFRAME (selected_frame);
-  context = get_frame_dc (f);
-  old_font = SelectObject (context,
-                          ((W32FontStruct *) (font->font.font))->hfont);
-
   glyphs = alloca (max_glyphs * sizeof (WORD));
   clusters = alloca (nchars * sizeof (WORD));
   attributes = alloca (max_glyphs * sizeof (SCRIPT_VISATTR));
   advances = alloca (max_glyphs * sizeof (int));
   offsets = alloca (max_glyphs * sizeof (GOFFSET));
-  bzero (&transform, sizeof (transform));
-  transform.eM11.value = 1;
-  transform.eM22.value = 1;
 
   for (i = 0; i < nitems; i++)
     {
       int nglyphs, nchars_in_run;
       nchars_in_run = items[i+1].iCharPos - items[i].iCharPos;
+      /* Force ScriptShape to generate glyphs in the same order as
+        they are in the input LGSTRING, which is in the logical
+        order.  */
+      items[i].a.fLogicalOrder = 1;
 
+      /* Context may be NULL here, in which case the cache should be
+         used without needing to select the font.  */
       result = ScriptShape (context, &(uniscribe_font->cache),
                            chars + items[i].iCharPos, nchars_in_run,
                            max_glyphs - done_glyphs, &(items[i].a),
                            glyphs, clusters, attributes, &nglyphs);
+
+      if (result == E_PENDING && !context)
+       {
+         /* This assumes the selected frame is on the same display as the
+            one we are drawing.  It would be better for the frame to be
+            passed in.  */
+         f = XFRAME (selected_frame);
+         context = get_frame_dc (f);
+         old_font = SelectObject (context, FONT_HANDLE (font));
+
+         result = ScriptShape (context, &(uniscribe_font->cache),
+                               chars + items[i].iCharPos, nchars_in_run,
+                               max_glyphs - done_glyphs, &(items[i].a),
+                               glyphs, clusters, attributes, &nglyphs);
+       }
+
       if (result == E_OUTOFMEMORY)
        {
          /* Need a bigger lgstring.  */
          lgstring = Qnil;
          break;
        }
-      else if (result)
+      else if (FAILED (result))
        {
          /* Can't shape this run - return results so far if any.  */
          break;
        }
+      else if (items[i].a.fNoGlyphIndex)
+       {
+         /* Glyph indices not supported by this font (or OS), means we
+            can't really do any meaningful shaping.  */
+         break;
+       }
       else
        {
          result = ScriptPlace (context, &(uniscribe_font->cache),
                                glyphs, nglyphs, attributes, &(items[i].a),
                                advances, offsets, &overall_metrics);
-         if (!result)
+         if (result == E_PENDING && !context)
            {
-             int j, nclusters;
+             /* Cache not complete...  */
+             f = XFRAME (selected_frame);
+             context = get_frame_dc (f);
+             old_font = SelectObject (context, FONT_HANDLE (font));
+
+             result = ScriptPlace (context, &(uniscribe_font->cache),
+                                   glyphs, nglyphs, attributes, &(items[i].a),
+                                   advances, offsets, &overall_metrics);
+           }
+          if (SUCCEEDED (result))
+           {
+             int j, from, to, adj_offset = 0;
+
+             from = 0;
+             to = from;
 
              for (j = 0; j < nglyphs; j++)
                {
                  int lglyph_index = j + done_glyphs;
                  Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, lglyph_index);
-                 GLYPHMETRICS metrics;
+                 ABC char_metric;
+                 unsigned gl;
 
                  if (NILP (lglyph))
                    {
                      lglyph = Fmake_vector (make_number (LGLYPH_SIZE), Qnil);
                      LGSTRING_SET_GLYPH (lgstring, lglyph_index, lglyph);
                    }
-                 LGLYPH_SET_CODE (lglyph, glyphs[j]);
+                 /* Copy to a 32-bit data type to shut up the
+                    compiler warning in LGLYPH_SET_CODE about
+                    comparison being always false.  */
+                 gl = glyphs[j];
+                 LGLYPH_SET_CODE (lglyph, gl);
+
+                 /* Detect clusters, for linking codes back to
+                    characters.  */
+                 if (attributes[j].fClusterStart)
+                   {
+                     while (from < nchars_in_run && clusters[from] < j)
+                       from++;
+                     if (from >= nchars_in_run)
+                       from = to = nchars_in_run - 1;
+                     else
+                       {
+                         int k;
+                         to = nchars_in_run - 1;
+                         for (k = from + 1; k < nchars_in_run; k++)
+                           {
+                             if (clusters[k] > j)
+                               {
+                                 to = k - 1;
+                                 break;
+                               }
+                           }
+                       }
+
+                     /* For RTL text, the Uniscribe shaper prepares
+                        the values in ADVANCES array for layout in
+                        reverse order, whereby "advance width" is
+                        applied to move the pen in reverse direction
+                        and _before_ drawing the glyph.  Since we
+                        draw glyphs in their normal left-to-right
+                        order, we need to adjust the coordinates of
+                        each non-base glyph in a grapheme cluster via
+                        X-OFF component of the gstring's ADJUSTMENT
+                        sub-vector.  This loop computes, for each
+                        grapheme cluster, the initial value of the
+                        adjustment for the base character, which is
+                        then updated for each successive glyph in the
+                        grapheme cluster.  */
+                     if (items[i].a.fRTL)
+                       {
+                         int j1 = j;
+
+                         adj_offset = 0;
+                         while (j1 < nglyphs && !attributes[j1].fClusterStart)
+                           {
+                             adj_offset += advances[j1];
+                             j1++;
+                           }
+                       }
+                   }
+
+                 LGLYPH_SET_CHAR (lglyph, chars[items[i].iCharPos
+                                                + from]);
+                 LGLYPH_SET_FROM (lglyph, items[i].iCharPos + from);
+                 LGLYPH_SET_TO (lglyph, items[i].iCharPos + to);
+
+                 /* Metrics.  */
                  LGLYPH_SET_WIDTH (lglyph, advances[j]);
-                 if (offsets[j].du || offsets[j].dv)
+                 LGLYPH_SET_ASCENT (lglyph, font->ascent);
+                 LGLYPH_SET_DESCENT (lglyph, font->descent);
+
+                 result = ScriptGetGlyphABCWidth (context,
+                                                  &(uniscribe_font->cache),
+                                                  glyphs[j], &char_metric);
+                 if (result == E_PENDING && !context)
                    {
-                     Lisp_Object vec;
-                     /* Convert from logical inches.  */
-                     int dpi = FRAME_W32_DISPLAY_INFO (f)->resy;
-                     int dx = (int)(offsets[j].du * dpi / 72.27 + 0.5);
-                     int dy = (int)(offsets[j].dv * dpi / 72.27 + 0.5);
-                     vec = Fmake_vector (make_number (3), Qnil);
-                     ASET (vec, 0, make_number (dx));
-                     ASET (vec, 1, make_number (dy));
-                     /* Based on what ftfont.c does... */
-                     ASET (vec, 2, make_number (advances[j]));
-                     LGLYPH_SET_ADJUSTMENT (lglyph, vec);
+                     /* Cache incomplete... */
+                     f = XFRAME (selected_frame);
+                     context = get_frame_dc (f);
+                     old_font = SelectObject (context, FONT_HANDLE (font));
+                     result = ScriptGetGlyphABCWidth (context,
+                                                      &(uniscribe_font->cache),
+                                                      glyphs[j], &char_metric);
                    }
-                 else
-                   LGLYPH_SET_ADJUSTMENT (lglyph, Qnil);
 
-                 if (GetGlyphOutlineW (context, glyphs[j],
-                                       GGO_METRICS | GGO_GLYPH_INDEX,
-                                       &metrics, 0, NULL, &transform)
-                     != GDI_ERROR)
+                 if (SUCCEEDED (result))
                    {
-                     LGLYPH_SET_LBEARING (lglyph, metrics.gmptGlyphOrigin.x);
-                     LGLYPH_SET_RBEARING (lglyph,
-                                          metrics.gmBlackBoxX
-                                          + metrics.gmptGlyphOrigin.x);
-                     LGLYPH_SET_ASCENT (lglyph, metrics.gmBlackBoxY);
-                     LGLYPH_SET_DESCENT (lglyph,
-                                         (metrics.gmCellIncY
-                                          - metrics.gmptGlyphOrigin.y
-                                          - metrics.gmBlackBoxY));
+                     int lbearing = char_metric.abcA;
+                     int rbearing = char_metric.abcA + char_metric.abcB;
+
+                     LGLYPH_SET_LBEARING (lglyph, lbearing);
+                     LGLYPH_SET_RBEARING (lglyph, rbearing);
                    }
                  else
                    {
-                     /* Defaults based on what we know from elsewhere.  */
                      LGLYPH_SET_LBEARING (lglyph, 0);
                      LGLYPH_SET_RBEARING (lglyph, advances[j]);
-                     LGLYPH_SET_ASCENT (lglyph, font->ascent);
-                     LGLYPH_SET_DESCENT (lglyph, font->descent);
                    }
-               }
-
-             /* Set character codes as indicated in clusters.  */
-             for (j = 0; j < nchars_in_run - 1; j++)
-               {
-                 int k, start, end;
-                 wchar_t this_char = *(chars + items[i].iCharPos + j);
 
-                 start = clusters[i];
-                 end = clusters[i+1];
-                 if (start > end)
+                 if (offsets[j].du || offsets[j].dv
+                     /* For non-base glyphs of RTL grapheme clusters,
+                        adjust the X offset even if both DU and DV
+                        are zero.  */
+                     || (!attributes[j].fClusterStart && items[i].a.fRTL))
                    {
-                     int temp = start;
-                     start = end;
-                     end = temp;
+                     Lisp_Object vec;
+                     vec = Fmake_vector (make_number (3), Qnil);
+                     if (items[i].a.fRTL)
+                       {
+                         /* Empirically, it looks like Uniscribe
+                            interprets DU in reverse direction for
+                            RTL clusters.  E.g., if we don't reverse
+                            the direction, the Hebrew point HOLAM is
+                            drawn above the right edge of the base
+                            consonant, instead of above the left edge.  */
+                         ASET (vec, 0, make_number (-offsets[j].du
+                                                    + adj_offset));
+                         /* Update the adjustment value for the width
+                            advance of the glyph we just emitted.  */
+                         adj_offset -= 2 * advances[j];
+                       }
+                     else
+                       ASET (vec, 0, make_number (offsets[j].du + adj_offset));
+                     /* In the font definition coordinate system, the
+                        Y coordinate points up, while in our screen
+                        coordinates Y grows downwards.  So we need to
+                        reverse the sign of Y-OFFSET here.  */
+                     ASET (vec, 1, make_number (-offsets[j].dv));
+                     /* Based on what ftfont.c does... */
+                     ASET (vec, 2, make_number (advances[j]));
+                     LGLYPH_SET_ADJUSTMENT (lglyph, vec);
                    }
-                 for (k = start; k < end; k++)
+                 else
                    {
-                     Lisp_Object lglyph = LGSTRING_GLYPH (lgstring,
-                                                          done_glyphs + k);
-                     LGLYPH_SET_CHAR (lglyph, this_char);
+                     LGLYPH_SET_ADJUSTMENT (lglyph, Qnil);
+                     /* Update the adjustment value to compensate for
+                        the width of the base character.  */
+                     if (items[i].a.fRTL)
+                       adj_offset -= advances[j];
                    }
                }
-             /* Last one until end (or beginning) of string.  */
-             {
-               int k, start, end;
-               wchar_t last_char = *(chars + items[i].iCharPos
-                                     + nchars_in_run - 1);
-               start = clusters[nchars_in_run - 1];
-               end = nglyphs;
-               if (start < clusters[nchars_in_run - 2])
-                 {
-                   end = start + 1;
-                   start = 0;
-                 }
-               for (k = start; k < end; k++)
-                 {
-                   Lisp_Object lglyph = LGSTRING_GLYPH (lgstring,
-                                                        done_glyphs + k);
-                   LGLYPH_SET_CHAR (lglyph, last_char);
-                 }
-             }
            }
        }
       done_glyphs += nglyphs;
-      done_chars += nchars_in_run;
     }
 
   xfree (items);
-  SelectObject (context, old_font);
-  release_frame_dc (f, context);
+
+  if (context)
+    {
+      SelectObject (context, old_font);
+      release_frame_dc (f, context);
+    }
 
   if (NILP (lgstring))
     return Qnil;
   else
-    return make_number (done_chars);
+    return make_number (done_glyphs);
 }
 
 /* Uniscribe implementation of encode_char for font backend.
-   Return a glyph code of FONT for characer C (Unicode code point).
+   Return a glyph code of FONT for character C (Unicode code point).
    If FONT doesn't have such a glyph, return FONT_INVALID_CODE.  */
 static unsigned
-uniscribe_encode_char (font, c)
-     struct font *font;
-     int c;
+uniscribe_encode_char (struct font *font, int c)
 {
-  wchar_t chars[1];
-  WORD indices[1];
-  HDC context;
-  struct frame *f;
-  HFONT old_font;
-  DWORD retval;
-
-  /* TODO: surrogates.  */
-  if (c > 0xFFFF)
-    return FONT_INVALID_CODE;
-
-  chars[0] = (wchar_t) c;
+  HDC context = NULL;
+  struct frame *f = NULL;
+  HFONT old_font = NULL;
+  unsigned code = FONT_INVALID_CODE;
+  wchar_t ch[2];
+  int len;
+  SCRIPT_ITEM* items;
+  int nitems;
+  struct uniscribe_font_info *uniscribe_font
+    = (struct uniscribe_font_info *)font;
 
-  /* Use selected frame until API is updated to pass the frame.  */
-  f = XFRAME (selected_frame);
-  context = get_frame_dc (f);
-  old_font = SelectObject (context,
-                           ((W32FontStruct *)(font->font.font))->hfont);
+  if (c < 0x10000)
+    {
+      ch[0] = (wchar_t) c;
+      len = 1;
+    }
+  else
+    {
+      DWORD surrogate = c - 0x10000;
 
-  retval = GetGlyphIndicesW (context, chars, 1, indices,
-                            GGI_MARK_NONEXISTING_GLYPHS);
+      /* High surrogate: U+D800 - U+DBFF.  */
+      ch[0] = 0xD800 + ((surrogate >> 10) & 0x03FF);
+      /* Low surrogate: U+DC00 - U+DFFF.  */
+      ch[1] = 0xDC00 + (surrogate & 0x03FF);
+      len = 2;
+    }
 
-  SelectObject (context, old_font);
-  release_frame_dc (f, context);
+  /* Non BMP characters must be handled by the uniscribe shaping
+     engine as GDI functions (except blindly displaying lines of
+     Unicode text) and the promising looking ScriptGetCMap do not
+     convert surrogate pairs to glyph indexes correctly.  */
+    {
+      items = (SCRIPT_ITEM *) alloca (sizeof (SCRIPT_ITEM) * 2 + 1);
+      if (SUCCEEDED (ScriptItemize (ch, len, 2, NULL, NULL, items, &nitems)))
+       {
+         HRESULT result;
+          /* Surrogates seem to need 2 here, even though only one glyph is
+            returned.  Indic characters can also produce 2 or more glyphs for
+            a single code point, but they need to use uniscribe_shape
+            above for correct display.  */
+          WORD glyphs[2], clusters[2];
+          SCRIPT_VISATTR attrs[2];
+          int nglyphs;
+
+         /* Force ScriptShape to generate glyphs in the logical
+            order.  */
+         items[0].a.fLogicalOrder = 1;
+
+          result = ScriptShape (context, &(uniscribe_font->cache),
+                                ch, len, 2, &(items[0].a),
+                                glyphs, clusters, attrs, &nglyphs);
+
+          if (result == E_PENDING)
+            {
+              /* Use selected frame until API is updated to pass
+                 the frame.  */
+              f = XFRAME (selected_frame);
+              context = get_frame_dc (f);
+              old_font = SelectObject (context, FONT_HANDLE (font));
+              result = ScriptShape (context, &(uniscribe_font->cache),
+                                    ch, len, 2, &(items[0].a),
+                                    glyphs, clusters, attrs, &nglyphs);
+            }
+
+          if (SUCCEEDED (result) && nglyphs == 1)
+            {
+             /* Some fonts return .notdef glyphs instead of failing.
+                (TrueType spec reserves glyph code 0 for .notdef)  */
+             if (glyphs[0])
+               code = glyphs[0];
+            }
+          else if (SUCCEEDED (result) || result == E_OUTOFMEMORY)
+            {
+              /* This character produces zero or more than one glyph
+                 when shaped. But we still need the return from here
+                 to be valid for the shaping engine to be invoked
+                 later.  */
+              result = ScriptGetCMap (context, &(uniscribe_font->cache),
+                                      ch, len, 0, glyphs);
+              if (SUCCEEDED (result) && glyphs[0])
+                code = glyphs[0];
+            }
+       }
+    }
+    if (context)
+      {
+       SelectObject (context, old_font);
+       release_frame_dc (f, context);
+      }
 
-  if (retval == 1)
-    return indices[0] == 0xFFFF ? FONT_INVALID_CODE : indices[0];
-  else
-    return FONT_INVALID_CODE;
+    return code;
 }
 
 /*
@@ -480,12 +619,9 @@ uniscribe_encode_char (font, c)
    Adds the name of opentype fonts to a Lisp list (passed in as the
    lParam arg). */
 static int CALLBACK
-add_opentype_font_name_to_list (logical_font, physical_font, font_type,
-                                list_object)
-     ENUMLOGFONTEX *logical_font;
-     NEWTEXTMETRICEX *physical_font;
-     DWORD font_type;
-     LPARAM list_object;
+add_opentype_font_name_to_list (ENUMLOGFONTEX *logical_font,
+                               NEWTEXTMETRICEX *physical_font,
+                               DWORD font_type, LPARAM list_object)
 {
   Lisp_Object* list = (Lisp_Object *) list_object;
   Lisp_Object family;
@@ -501,8 +637,14 @@ add_opentype_font_name_to_list (logical_font, physical_font, font_type,
       && font_type != TRUETYPE_FONTTYPE)
     return 1;
 
-  family = intern_downcase (logical_font->elfLogFont.lfFaceName,
-                            strlen (logical_font->elfLogFont.lfFaceName));
+  /* Skip fonts that have no Unicode coverage.  */
+  if (!physical_font->ntmFontSig.fsUsb[3]
+      && !physical_font->ntmFontSig.fsUsb[2]
+      && !physical_font->ntmFontSig.fsUsb[1]
+      && !(physical_font->ntmFontSig.fsUsb[0] & 0x3fffffff))
+    return 1;
+
+  family = intern_font_name (logical_font->elfLogFont.lfFaceName);
   if (! memq_no_quit (family, *list))
     *list = Fcons (family, *list);
 
@@ -543,16 +685,13 @@ add_opentype_font_name_to_list (logical_font, physical_font, font_type,
     STR[4] = '\0';                                           \
   } while (0)
 
-static char* NOTHING = "    ";
-
-#define SNAME(VAL) SDATA (STRINGP (VAL) ? VAL : SYMBOL_NAME (VAL))
+#define SNAME(VAL) SDATA (SYMBOL_NAME (VAL))
 
 /* Check if font supports the otf script/language/features specified.
    OTF_SPEC is in the format
      (script lang [(gsub_feature ...)|nil] [(gpos_feature ...)]?) */
-int uniscribe_check_otf (font, otf_spec)
-     LOGFONT *font;
-     Lisp_Object otf_spec;
+int
+uniscribe_check_otf (LOGFONT *font, Lisp_Object otf_spec)
 {
   Lisp_Object script, lang, rest;
   Lisp_Object features[2];
@@ -561,12 +700,11 @@ int uniscribe_check_otf (font, otf_spec)
   struct frame * f;
   HDC context;
   HFONT check_font, old_font;
-  DWORD table;
   int i, retval = 0;
   struct gcpro gcpro1;
 
   /* Check the spec is in the right format.  */
-  if (!CONSP (otf_spec) || Flength (otf_spec) < 3)
+  if (!CONSP (otf_spec) || XINT (Flength (otf_spec)) < 3)
     return 0;
 
   /* Break otf_spec into its components.  */
@@ -637,10 +775,12 @@ int uniscribe_check_otf (font, otf_spec)
              OTF_INT16_VAL (tbl, scriptlist_table + 6 + j * 6, &script_table);
              break;
            }
+#if 0    /* Causes false positives.  */
          /* If there is a DFLT script defined in the font, use it
             if the specified script is not found.  */
          else if (script_id == default_script)
            OTF_INT16_VAL (tbl, scriptlist_table + 6 + j * 6, &script_table);
+#endif
        }
       /* If no specific or default script table was found, then this font
         does not support the script.  */
@@ -826,6 +966,7 @@ font_table_error:
 struct font_driver uniscribe_font_driver =
   {
     0, /* Quniscribe */
+    0, /* case insensitive */
     w32font_get_cache,
     uniscribe_list,
     uniscribe_match,
@@ -844,17 +985,21 @@ struct font_driver uniscribe_font_driver =
     NULL, /* get_outline */
     NULL, /* free_outline */
     NULL, /* anchor_point */
-    uniscribe_otf_capability, /* Defined so (font-get FONTOBJ :otf) works.  */ 
+    uniscribe_otf_capability, /* Defined so (font-get FONTOBJ :otf) works.  */
     NULL, /* otf_drive - use shape instead.  */
     NULL, /* start_for_frame */
     NULL, /* end_for_frame */
-    uniscribe_shape
+    uniscribe_shape,
+    NULL, /* check */
+    NULL, /* get_variation_glyphs */
+    NULL, /* filter_properties */
+    NULL, /* cached_font_ok */
   };
 
 /* Note that this should be called at every startup, not just when dumping,
    as it needs to test for the existence of the Uniscribe library.  */
 void
-syms_of_w32uniscribe ()
+syms_of_w32uniscribe (void)
 {
   HMODULE uniscribe;
 
@@ -872,8 +1017,3 @@ syms_of_w32uniscribe ()
 
   register_font_driver (&uniscribe_font_driver, NULL);
 }
-
-#endif /* USE_FONT_BACKEND  */
-
-/* arch-tag: 9530f0e1-7471-47dd-a780-94330af87ea0
-   (do not change this comment) */