Add bug reference.
[bpt/emacs.git] / src / w32uniscribe.c
index 3208621..1b5984f 100644 (file)
@@ -3,10 +3,10 @@
 
 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,11 +14,8 @@ 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
@@ -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"
@@ -79,14 +77,18 @@ static Lisp_Object
 uniscribe_list (frame, font_spec)
      Lisp_Object frame, 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;
 {
-  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
@@ -112,32 +114,36 @@ uniscribe_list_family (frame)
   return list;
 }
 
-static struct font *
+static Lisp_Object
 uniscribe_open (f, font_entity, pixel_size)
      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
@@ -149,7 +155,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);
 }
@@ -168,8 +174,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);
@@ -202,27 +207,25 @@ uniscribe_shape (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));
   for (i = 0; i < nchars; i++)
     {
@@ -238,177 +241,209 @@ 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 = (SCRIPT_ITEM *) 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);
+  /* TODO: When we get BIDI support, we need to call ScriptLayout here.
+     Requires that we know the surrounding context.  */
 
   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;
+      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)
+      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)
+           {
+             /* 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;
+             int j, nclusters, from, to;
+
+             from = rtl > 0 ? 0 : nchars_in_run - 1;
+             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 >= 0 && from < nchars_in_run
+                            && clusters[from] < j)
+                       from += rtl;
+                     if (from < 0)
+                       from = to = 0;
+                     else if (from >= nchars_in_run)
+                       from = to = nchars_in_run - 1;
+                     else
+                       {
+                         int k;
+                         to = rtl > 0 ? nchars_in_run - 1 : 0;
+                         for (k = from + rtl; k >= 0 && k < nchars_in_run;
+                              k += rtl)
+                           {
+                             if (clusters[k] > j)
+                               {
+                                 to = k - 1;
+                                 break;
+                               }
+                           }
+                       }
+                   }
+
+                 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));
+                     LGLYPH_SET_LBEARING (lglyph, char_metric.abcA);
+                     LGLYPH_SET_RBEARING (lglyph, (char_metric.abcA
+                                                   + char_metric.abcB));
                    }
                  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)
-                   {
-                     int temp = start;
-                     start = end;
-                     end = temp;
-                   }
-                 for (k = start; k < end; k++)
+                 if (offsets[j].du || offsets[j].dv)
                    {
-                     Lisp_Object lglyph = LGSTRING_GLYPH (lgstring,
-                                                          done_glyphs + k);
-                     LGLYPH_SET_CHAR (lglyph, this_char);
+                     Lisp_Object vec;
+                     vec = Fmake_vector (make_number (3), Qnil);
+                     ASET (vec, 0, make_number (offsets[j].du));
+                     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);
                    }
+                 else
+                   LGLYPH_SET_ADJUSTMENT (lglyph, Qnil);
                }
-             /* 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.
@@ -419,35 +454,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,
-                           ((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;
+
+          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;
 }
 
 /*
@@ -501,8 +594,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);
 
@@ -545,7 +644,7 @@ add_opentype_font_name_to_list (logical_font, physical_font, font_type,
 
 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
@@ -637,10 +736,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 +927,7 @@ font_table_error:
 struct font_driver uniscribe_font_driver =
   {
     0, /* Quniscribe */
+    0, /* case insensitive */
     w32font_get_cache,
     uniscribe_list,
     uniscribe_match,
@@ -873,7 +975,5 @@ 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) */