Implement symbol cmap in ft and ot fonts

Fixes https://github.com/behdad/harfbuzz/issues/236
Fixes https://bugs.chromium.org/p/chromium/issues/detail?id=627953
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index 6c6749c..eaa1311 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -70,11 +70,12 @@
 {
   FT_Face ft_face;
   int load_flags;
+  bool symbol; /* Whether selected cmap is symbol cmap. */
   bool unref; /* Whether to destroy ft_face when done. */
 };
 
 static hb_ft_font_t *
-_hb_ft_font_create (FT_Face ft_face, bool unref)
+_hb_ft_font_create (FT_Face ft_face, bool symbol, bool unref)
 {
   hb_ft_font_t *ft_font = (hb_ft_font_t *) calloc (1, sizeof (hb_ft_font_t));
 
@@ -82,6 +83,7 @@
     return NULL;
 
   ft_font->ft_face = ft_face;
+  ft_font->symbol = symbol;
   ft_font->unref = unref;
 
   ft_font->load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
@@ -171,7 +173,21 @@
   unsigned int g = FT_Get_Char_Index (ft_font->ft_face, unicode);
 
   if (unlikely (!g))
-    return false;
+  {
+    if (unlikely (ft_font->symbol) && unicode <= 0x00FFu)
+    {
+      /* For symbol-encoded OpenType fonts, we duplicate the
+       * U+F000..F0FF range at U+0000..U+00FF.  That's what
+       * Windows seems to do, and that's hinted about at:
+       * http://www.microsoft.com/typography/otspec/recom.htm
+       * under "Non-Standard (Symbol) Fonts". */
+      g = FT_Get_Char_Index (ft_font->ft_face, 0xF000u + unicode);
+      if (!g)
+	return false;
+    }
+    else
+      return false;
+  }
 
   *glyph = g;
   return true;
@@ -450,9 +466,11 @@
 #endif
   };
 
+  bool symbol = ft_face->charmap && ft_face->charmap->encoding == FT_ENCODING_MS_SYMBOL;
+
   hb_font_set_funcs (font,
 		     funcs,
-		     _hb_ft_font_create (ft_face, unref),
+		     _hb_ft_font_create (ft_face, symbol, unref),
 		     (hb_destroy_func_t) _hb_ft_font_destroy);
 }
 
@@ -681,7 +699,8 @@
     return;
   }
 
-  FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE);
+  if (FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE))
+    FT_Select_Charmap (ft_face, FT_ENCODING_MS_SYMBOL);
 
   FT_Set_Char_Size (ft_face,
 		    abs (font->x_scale), abs (font->y_scale),
diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc
index 39fc849..0b7e31b 100644
--- a/src/hb-ot-font.cc
+++ b/src/hb-ot-font.cc
@@ -215,6 +215,28 @@
   return typed_obj->get_glyph (codepoint, glyph);
 }
 
+template <typename Type>
+static inline bool get_glyph_from_symbol (const void *obj,
+					  hb_codepoint_t codepoint,
+					  hb_codepoint_t *glyph)
+{
+  const Type *typed_obj = (const Type *) obj;
+  if (likely (typed_obj->get_glyph (codepoint, glyph)))
+    return true;
+
+  if (codepoint <= 0x00FFu)
+  {
+    /* For symbol-encoded OpenType fonts, we duplicate the
+     * U+F000..F0FF range at U+0000..U+00FF.  That's what
+     * Windows seems to do, and that's hinted about at:
+     * http://www.microsoft.com/typography/otspec/recom.htm
+     * under "Non-Standard (Symbol) Fonts". */
+    return typed_obj->get_glyph (0xF000u + codepoint, glyph);
+  }
+
+  return false;
+}
+
 struct hb_ot_face_cmap_accelerator_t
 {
   hb_cmap_get_glyph_func_t get_glyph_func;
@@ -231,6 +253,7 @@
     const OT::CmapSubtable *subtable = NULL;
     const OT::CmapSubtableFormat14 *subtable_uvs = NULL;
 
+    bool symbol = false;
     /* 32-bit subtables. */
     if (!subtable) subtable = cmap->find_subtable (3, 10);
     if (!subtable) subtable = cmap->find_subtable (0, 6);
@@ -241,7 +264,7 @@
     if (!subtable) subtable = cmap->find_subtable (0, 2);
     if (!subtable) subtable = cmap->find_subtable (0, 1);
     if (!subtable) subtable = cmap->find_subtable (0, 0);
-    if (!subtable) subtable = cmap->find_subtable (3, 0);
+    if (!subtable)(subtable = cmap->find_subtable (3, 0)) && (symbol = true);
     /* Meh. */
     if (!subtable) subtable = &OT::Null(OT::CmapSubtable);
 
@@ -258,18 +281,21 @@
     this->uvs_table = subtable_uvs;
 
     this->get_glyph_data = subtable;
-    switch (subtable->u.format) {
-    /* Accelerate format 4 and format 12. */
-    default: this->get_glyph_func = get_glyph_from<OT::CmapSubtable>;		break;
-    case 12: this->get_glyph_func = get_glyph_from<OT::CmapSubtableFormat12>;	break;
-    case  4:
-      {
-        this->format4_accel.init (&subtable->u.format4);
-	this->get_glyph_data = &this->format4_accel;
-        this->get_glyph_func = this->format4_accel.get_glyph_func;
+    if (unlikely (symbol))
+      this->get_glyph_func = get_glyph_from_symbol<OT::CmapSubtable>;
+    else
+      switch (subtable->u.format) {
+      /* Accelerate format 4 and format 12. */
+      default: this->get_glyph_func = get_glyph_from<OT::CmapSubtable>;		break;
+      case 12: this->get_glyph_func = get_glyph_from<OT::CmapSubtableFormat12>;	break;
+      case  4:
+	{
+	  this->format4_accel.init (&subtable->u.format4);
+	  this->get_glyph_data = &this->format4_accel;
+	  this->get_glyph_func = this->format4_accel.get_glyph_func;
+	}
+	break;
       }
-      break;
-    }
   }
 
   inline void fini (void)