]> code.delx.au - gnu-emacs/blobdiff - src/w32uniscribe.c
* charset.c (load_charset_map_from_file)
[gnu-emacs] / src / w32uniscribe.c
index 366287e8de6a78ef7fb53d2856ed412c7c162788..f2bd0f44b50fcff4b42d5541c244a1cf43db8560 100644 (file)
@@ -1,5 +1,5 @@
 /* Font backend for the Microsoft W32 Uniscribe API.
-   Copyright (C) 2008 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -22,11 +22,12 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
    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>
 #include <usp10.h>
+#include <setjmp.h>
 
 #include "lisp.h"
 #include "w32term.h"
@@ -34,6 +35,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "dispextern.h"
 #include "character.h"
 #include "charset.h"
+#include "composite.h"
 #include "fontset.h"
 #include "font.h"
 #include "w32font.h"
@@ -77,7 +79,7 @@ uniscribe_list (frame, font_spec)
      Lisp_Object frame, font_spec;
 {
   Lisp_Object fonts = w32font_list_internal (frame, font_spec, 1);
-  font_add_log ("uniscribe-list", font_spec, fonts);
+  FONT_ADD_LOG ("uniscribe-list", font_spec, fonts);
   return fonts;
 }
 
@@ -86,7 +88,7 @@ uniscribe_match (frame, font_spec)
      Lisp_Object frame, font_spec;
 {
   Lisp_Object entity = w32font_match_internal (frame, font_spec, 1);
-  font_add_log ("uniscribe-match", font_spec, entity);
+  FONT_ADD_LOG ("uniscribe-match", font_spec, entity);
   return entity;
 }
 
@@ -120,10 +122,13 @@ uniscribe_open (f, font_entity, pixel_size)
      int pixel_size;
 {
   Lisp_Object font_object
-    = font_make_object (VECSIZE (struct uniscribe_font_info));
+    = font_make_object (VECSIZE (struct uniscribe_font_info),
+                       font_entity, pixel_size);
   struct uniscribe_font_info *uniscribe_font
     = (struct uniscribe_font_info *) XFONT_OBJECT (font_object);
 
+  ASET (font_object, FONT_TYPE_INDEX, Quniscribe);
+
   if (!w32font_open_internal (f, font_entity, pixel_size, font_object))
     {
       return Qnil;
@@ -131,6 +136,10 @@ uniscribe_open (f, font_entity, pixel_size)
 
   /* 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.props[FONT_FORMAT_INDEX] = Qopentype;
   uniscribe_font->w32_font.font.driver = &uniscribe_font_driver;
@@ -147,7 +156,7 @@ uniscribe_close (f, font)
     = (struct uniscribe_font_info *) font;
 
   if (uniscribe_font->cache)
-    ScriptFreeCache (&uniscribe_font->cache);
+    ScriptFreeCache (&(uniscribe_font->cache));
 
   w32font_close (f, font);
 }
@@ -203,22 +212,20 @@ uniscribe_shape (lgstring)
   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);
+  max_glyphs = nchars = LGSTRING_GLYPH_LEN (lgstring);
   done_glyphs = 0;
   chars = (wchar_t *) alloca (nchars * sizeof (wchar_t));
   for (i = 0; i < nchars; i++)
@@ -236,9 +243,8 @@ uniscribe_shape (lgstring)
      can be treated together.  First try a single run.  */
   max_items = 2;
   items = (SCRIPT_ITEM *) xmalloc (sizeof (SCRIPT_ITEM) * max_items + 1);
-  bzero (&control, sizeof (control));
 
-  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.  */
@@ -247,8 +253,7 @@ uniscribe_shape (lgstring)
                                        sizeof (SCRIPT_ITEM) * max_items + 1);
     }
 
-  /* 0 = success in Microsoft's backwards world.  */
-  if (result)
+  if (FAILED (result))
     {
       xfree (items);
       return Qnil;
@@ -257,35 +262,46 @@ uniscribe_shape (lgstring)
   /* TODO: When we get BIDI support, we need to call ScriptLayout here.
      Requires that we know the surrounding context.  */
 
-  f = XFRAME (selected_frame);
-  context = get_frame_dc (f);
-  old_font = SelectObject (context, FONT_HANDLE(font));
-
   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, rtl = items[i].a.fRTL ? -1 : 1;
       nchars_in_run = items[i+1].iCharPos - items[i].iCharPos;
 
+      /* 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) /* Failure.  */
+      else if (FAILED (result))
        {
          /* Can't shape this run - return results so far if any.  */
          break;
@@ -301,7 +317,18 @@ uniscribe_shape (lgstring)
          result = ScriptPlace (context, &(uniscribe_font->cache),
                                glyphs, nglyphs, attributes, &(items[i].a),
                                advances, offsets, &overall_metrics);
-         if (result == 0) /* Success.  */
+         if (result == E_PENDING && !context)
+           {
+             /* 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, nclusters, from, to;
 
@@ -313,13 +340,18 @@ uniscribe_shape (lgstring)
                  int lglyph_index = j + done_glyphs;
                  Lisp_Object lglyph = LGSTRING_GLYPH (lgstring, lglyph_index);
                  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)
@@ -360,8 +392,18 @@ uniscribe_shape (lgstring)
                  result = ScriptGetGlyphABCWidth (context,
                                                   &(uniscribe_font->cache),
                                                   glyphs[j], &char_metric);
+                 if (result == E_PENDING && !context)
+                   {
+                     /* 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);
+                   }
 
-                 if (result == 0) /* Success.  */
+                 if (SUCCEEDED (result))
                    {
                      LGLYPH_SET_LBEARING (lglyph, char_metric.abcA);
                      LGLYPH_SET_RBEARING (lglyph, (char_metric.abcA
@@ -385,14 +427,19 @@ uniscribe_shape (lgstring)
                    }
                  else
                    LGLYPH_SET_ADJUSTMENT (lglyph, Qnil);
-               }           }
+               }
+           }
        }
       done_glyphs += nglyphs;
     }
 
   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;
@@ -408,34 +455,93 @@ uniscribe_encode_char (font, c)
      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, FONT_HANDLE(font));
+  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;
+
+          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;
 }
 
 /*
@@ -496,8 +602,7 @@ add_opentype_font_name_to_list (logical_font, physical_font, font_type,
       && !(physical_font->ntmFontSig.fsUsb[0] & 0x3fffffff))
     return 1;
 
-  family = font_intern_prop (logical_font->elfLogFont.lfFaceName,
-                            strlen (logical_font->elfLogFont.lfFaceName), 1);
+  family = intern_font_name (logical_font->elfLogFont.lfFaceName);
   if (! memq_no_quit (family, *list))
     *list = Fcons (family, *list);
 
@@ -632,10 +737,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.  */