]> code.delx.au - gnu-emacs/blobdiff - src/w32uniscribe.c
Fix bug ref
[gnu-emacs] / src / w32uniscribe.c
index 9e75b671616f95e2e68cc65d1ad8928943a944a8..5d160b9d42f22dad437e8010a485c4d37370784c 100644 (file)
@@ -1,5 +1,5 @@
 /* Font backend for the Microsoft W32 Uniscribe API.
-   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2008-2012 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -22,7 +22,7 @@ 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>
@@ -51,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);
@@ -74,8 +69,7 @@ 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)
 {
   Lisp_Object fonts = w32font_list_internal (frame, font_spec, 1);
   FONT_ADD_LOG ("uniscribe-list", font_spec, fonts);
@@ -83,8 +77,7 @@ uniscribe_list (frame, font_spec)
 }
 
 static Lisp_Object
-uniscribe_match (frame, font_spec)
-     Lisp_Object frame, font_spec;
+uniscribe_match (Lisp_Object frame, Lisp_Object font_spec)
 {
   Lisp_Object entity = w32font_match_internal (frame, font_spec, 1);
   FONT_ADD_LOG ("uniscribe-match", font_spec, entity);
@@ -92,15 +85,14 @@ uniscribe_match (frame, font_spec)
 }
 
 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;
 
@@ -115,10 +107,7 @@ uniscribe_list_family (frame)
 }
 
 static Lisp_Object
-uniscribe_open (f, font_entity, pixel_size)
-     FRAME_PTR f;
-     Lisp_Object font_entity;
-     int pixel_size;
+uniscribe_open (FRAME_PTR f, Lisp_Object font_entity, int pixel_size)
 {
   Lisp_Object font_object
     = font_make_object (VECSIZE (struct uniscribe_font_info),
@@ -147,9 +136,7 @@ uniscribe_open (f, font_entity, pixel_size)
 }
 
 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;
@@ -163,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;
@@ -174,7 +160,7 @@ uniscribe_otf_capability (font)
 
   f = XFRAME (selected_frame);
   context = get_frame_dc (f);
-  old_font = SelectObject (context, FONT_HANDLE(font));
+  old_font = SelectObject (context, FONT_HANDLE (font));
 
   features = otf_features (context, "GSUB");
   XSETCAR (capability, features);
@@ -189,20 +175,20 @@ 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;
@@ -227,6 +213,9 @@ uniscribe_shape (lgstring)
   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
@@ -241,7 +230,7 @@ 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 + 1);
+  items = xmalloc (sizeof (SCRIPT_ITEM) * max_items + 1);
 
   while ((result = ScriptItemize (chars, nchars, max_items, NULL, NULL,
                                  items, &nitems)) == E_OUTOFMEMORY)
@@ -258,9 +247,6 @@ uniscribe_shape (lgstring)
       return Qnil;
     }
 
-  /* 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));
@@ -269,8 +255,12 @@ uniscribe_shape (lgstring)
 
   for (i = 0; i < nitems; i++)
     {
-      int nglyphs, nchars_in_run, rtl = items[i].a.fRTL ? -1 : 1;
+      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.  */
@@ -286,7 +276,7 @@ uniscribe_shape (lgstring)
             passed in.  */
          f = XFRAME (selected_frame);
          context = get_frame_dc (f);
-         old_font = SelectObject (context, FONT_HANDLE(font));
+         old_font = SelectObject (context, FONT_HANDLE (font));
 
          result = ScriptShape (context, &(uniscribe_font->cache),
                                chars + items[i].iCharPos, nchars_in_run,
@@ -321,7 +311,7 @@ uniscribe_shape (lgstring)
              /* Cache not complete...  */
              f = XFRAME (selected_frame);
              context = get_frame_dc (f);
-             old_font = SelectObject (context, FONT_HANDLE(font));
+             old_font = SelectObject (context, FONT_HANDLE (font));
 
              result = ScriptPlace (context, &(uniscribe_font->cache),
                                    glyphs, nglyphs, attributes, &(items[i].a),
@@ -329,9 +319,9 @@ uniscribe_shape (lgstring)
            }
           if (SUCCEEDED (result))
            {
-             int j, nclusters, from, to;
+             int j, from, to, adj_offset = 0;
 
-             from = rtl > 0 ? 0 : nchars_in_run - 1;
+             from = 0;
              to = from;
 
              for (j = 0; j < nglyphs; j++)
@@ -352,22 +342,19 @@ uniscribe_shape (lgstring)
                  gl = glyphs[j];
                  LGLYPH_SET_CODE (lglyph, gl);
 
-                 /* Detect clusters, for linking codes back to characters.  */
+                 /* 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)
+                     while (from < nchars_in_run && clusters[from] < j)
+                       from++;
+                     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)
+                         to = nchars_in_run - 1;
+                         for (k = from + 1; k < nchars_in_run; k++)
                            {
                              if (clusters[k] > j)
                                {
@@ -376,6 +363,32 @@ uniscribe_shape (lgstring)
                                }
                            }
                        }
+
+                     /* 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
@@ -396,7 +409,7 @@ uniscribe_shape (lgstring)
                      /* Cache incomplete... */
                      f = XFRAME (selected_frame);
                      context = get_frame_dc (f);
-                     old_font = SelectObject (context, FONT_HANDLE(font));
+                     old_font = SelectObject (context, FONT_HANDLE (font));
                      result = ScriptGetGlyphABCWidth (context,
                                                       &(uniscribe_font->cache),
                                                       glyphs[j], &char_metric);
@@ -404,9 +417,11 @@ uniscribe_shape (lgstring)
 
                  if (SUCCEEDED (result))
                    {
-                     LGLYPH_SET_LBEARING (lglyph, char_metric.abcA);
-                     LGLYPH_SET_RBEARING (lglyph, (char_metric.abcA
-                                                   + char_metric.abcB));
+                     int lbearing = char_metric.abcA;
+                     int rbearing = char_metric.abcA + char_metric.abcB;
+
+                     LGLYPH_SET_LBEARING (lglyph, lbearing);
+                     LGLYPH_SET_RBEARING (lglyph, rbearing);
                    }
                  else
                    {
@@ -414,18 +429,47 @@ uniscribe_shape (lgstring)
                      LGLYPH_SET_RBEARING (lglyph, advances[j]);
                    }
 
-                 if (offsets[j].du || offsets[j].dv)
+                 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))
                    {
                      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));
+                     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);
                    }
                  else
-                   LGLYPH_SET_ADJUSTMENT (lglyph, Qnil);
+                   {
+                     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];
+                   }
                }
            }
        }
@@ -447,12 +491,10 @@ uniscribe_shape (lgstring)
 }
 
 /* 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)
 {
   HDC context = NULL;
   struct frame *f = NULL;
@@ -483,7 +525,7 @@ uniscribe_encode_char (font, c)
 
   /* 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
+     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);
@@ -498,6 +540,10 @@ uniscribe_encode_char (font, c)
           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);
@@ -508,7 +554,7 @@ uniscribe_encode_char (font, c)
                  the frame.  */
               f = XFRAME (selected_frame);
               context = get_frame_dc (f);
-              old_font = SelectObject (context, FONT_HANDLE(font));
+              old_font = SelectObject (context, FONT_HANDLE (font));
               result = ScriptShape (context, &(uniscribe_font->cache),
                                     ch, len, 2, &(items[0].a),
                                     glyphs, clusters, attrs, &nglyphs);
@@ -517,7 +563,7 @@ uniscribe_encode_char (font, c)
           if (SUCCEEDED (result) && nglyphs == 1)
             {
              /* Some fonts return .notdef glyphs instead of failing.
-                (Truetype spec reserves glyph code 0 for .notdef)  */
+                (TrueType spec reserves glyph code 0 for .notdef)  */
              if (glyphs[0])
                code = glyphs[0];
             }
@@ -573,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;
@@ -594,7 +637,7 @@ add_opentype_font_name_to_list (logical_font, physical_font, font_type,
       && font_type != TRUETYPE_FONTTYPE)
     return 1;
 
-  /* Skip fonts that have no unicode coverage.  */
+  /* Skip fonts that have no Unicode coverage.  */
   if (!physical_font->ntmFontSig.fsUsb[3]
       && !physical_font->ntmFontSig.fsUsb[2]
       && !physical_font->ntmFontSig.fsUsb[1]
@@ -642,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 (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];
@@ -660,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.  */
@@ -946,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;
 
@@ -974,6 +1017,3 @@ syms_of_w32uniscribe ()
 
   register_font_driver (&uniscribe_font_driver, NULL);
 }
-
-/* arch-tag: 9530f0e1-7471-47dd-a780-94330af87ea0
-   (do not change this comment) */