1 /*
   2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   3  *
   4  * This code is free software; you can redistribute it and/or modify it
   5  * under the terms of the GNU General Public License version 2 only, as
   6  * published by the Free Software Foundation.  Oracle designates this
   7  * particular file as subject to the "Classpath" exception as provided
   8  * by Oracle in the LICENSE file that accompanied this code.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 // This file is available under and governed by the GNU General Public
  26 // License version 2 only, as published by the Free Software Foundation.
  27 // However, the following notice accompanied the original version of this
  28 // file:
  29 //
  30 /*
  31  * Copyright © 2012  Google, Inc.
  32  *
  33  *  This is part of HarfBuzz, a text shaping library.
  34  *
  35  * Permission is hereby granted, without written agreement and without
  36  * license or royalty fees, to use, copy, modify, and distribute this
  37  * software and its documentation for any purpose, provided that the
  38  * above copyright notice and the following two paragraphs appear in
  39  * all copies of this software.
  40  *
  41  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
  42  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
  43  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
  44  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
  45  * DAMAGE.
  46  *
  47  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  48  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  49  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  50  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  51  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  52  *
  53  * Google Author(s): Behdad Esfahbod
  54  */
  55 
  56 #ifndef HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
  57 #define HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
  58 
  59 #include "hb-private.hh"
  60 
  61 #include "hb-ot-shape-private.hh"
  62 #include "hb-ot-layout-gsub-table.hh"
  63 
  64 
  65 /* Features ordered the same as the entries in shaping_table rows,
  66  * followed by rlig.  Don't change. */
  67 static const hb_tag_t arabic_fallback_features[] =
  68 {
  69   HB_TAG('i','n','i','t'),
  70   HB_TAG('m','e','d','i'),
  71   HB_TAG('f','i','n','a'),
  72   HB_TAG('i','s','o','l'),
  73   HB_TAG('r','l','i','g'),
  74 };
  75 
  76 static OT::SubstLookup *
  77 arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUSED,
  78                                           hb_font_t *font,
  79                                           unsigned int feature_index)
  80 {
  81   OT::GlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1];
  82   OT::GlyphID substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1];
  83   unsigned int num_glyphs = 0;
  84 
  85   /* Populate arrays */
  86   for (hb_codepoint_t u = SHAPING_TABLE_FIRST; u < SHAPING_TABLE_LAST + 1; u++)
  87   {
  88     hb_codepoint_t s = shaping_table[u - SHAPING_TABLE_FIRST][feature_index];
  89     hb_codepoint_t u_glyph, s_glyph;
  90 
  91     if (!s ||
  92         !hb_font_get_glyph (font, u, 0, &u_glyph) ||
  93         !hb_font_get_glyph (font, s, 0, &s_glyph) ||
  94         u_glyph == s_glyph ||
  95         u_glyph > 0xFFFFu || s_glyph > 0xFFFFu)
  96       continue;
  97 
  98     glyphs[num_glyphs].set (u_glyph);
  99     substitutes[num_glyphs].set (s_glyph);
 100 
 101     num_glyphs++;
 102   }
 103 
 104   if (!num_glyphs)
 105     return NULL;
 106 
 107   /* Bubble-sort or something equally good!
 108    * May not be good-enough for presidential candidate interviews, but good-enough for us... */
 109   hb_stable_sort (&glyphs[0], num_glyphs, OT::GlyphID::cmp, &substitutes[0]);
 110 
 111   OT::Supplier<OT::GlyphID> glyphs_supplier      (glyphs, num_glyphs);
 112   OT::Supplier<OT::GlyphID> substitutes_supplier (substitutes, num_glyphs);
 113 
 114   /* Each glyph takes four bytes max, and there's some overhead. */
 115   char buf[(SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1) * 4 + 128];
 116   OT::hb_serialize_context_t c (buf, sizeof (buf));
 117   OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
 118   bool ret = lookup->serialize_single (&c,
 119                                        OT::LookupFlag::IgnoreMarks,
 120                                        glyphs_supplier,
 121                                        substitutes_supplier,
 122                                        num_glyphs);
 123   c.end_serialize ();
 124   /* TODO sanitize the results? */
 125 
 126   return ret ? c.copy<OT::SubstLookup> () : NULL;
 127 }
 128 
 129 static OT::SubstLookup *
 130 arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UNUSED,
 131                                             hb_font_t *font)
 132 {
 133   OT::GlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)];
 134   unsigned int first_glyphs_indirection[ARRAY_LENGTH_CONST (ligature_table)];
 135   unsigned int ligature_per_first_glyph_count_list[ARRAY_LENGTH_CONST (first_glyphs)];
 136   unsigned int num_first_glyphs = 0;
 137 
 138   /* We know that all our ligatures are 2-component */
 139   OT::GlyphID ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)];
 140   unsigned int component_count_list[ARRAY_LENGTH_CONST (ligature_list)];
 141   OT::GlyphID component_list[ARRAY_LENGTH_CONST (ligature_list) * 1/* One extra component per ligature */];
 142   unsigned int num_ligatures = 0;
 143 
 144   /* Populate arrays */
 145 
 146   /* Sort out the first-glyphs */
 147   for (unsigned int first_glyph_idx = 0; first_glyph_idx < ARRAY_LENGTH (first_glyphs); first_glyph_idx++)
 148   {
 149     hb_codepoint_t first_u = ligature_table[first_glyph_idx].first;
 150     hb_codepoint_t first_glyph;
 151     if (!hb_font_get_glyph (font, first_u, 0, &first_glyph))
 152       continue;
 153     first_glyphs[num_first_glyphs].set (first_glyph);
 154     ligature_per_first_glyph_count_list[num_first_glyphs] = 0;
 155     first_glyphs_indirection[num_first_glyphs] = first_glyph_idx;
 156     num_first_glyphs++;
 157   }
 158   hb_stable_sort (&first_glyphs[0], num_first_glyphs, OT::GlyphID::cmp, &first_glyphs_indirection[0]);
 159 
 160   /* Now that the first-glyphs are sorted, walk again, populate ligatures. */
 161   for (unsigned int i = 0; i < num_first_glyphs; i++)
 162   {
 163     unsigned int first_glyph_idx = first_glyphs_indirection[i];
 164 
 165     for (unsigned int second_glyph_idx = 0; second_glyph_idx < ARRAY_LENGTH (ligature_table[0].ligatures); second_glyph_idx++)
 166     {
 167       hb_codepoint_t second_u   = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].second;
 168       hb_codepoint_t ligature_u = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].ligature;
 169       hb_codepoint_t second_glyph, ligature_glyph;
 170       if (!second_u ||
 171           !hb_font_get_glyph (font, second_u,   0, &second_glyph) ||
 172           !hb_font_get_glyph (font, ligature_u, 0, &ligature_glyph))
 173         continue;
 174 
 175       ligature_per_first_glyph_count_list[i]++;
 176 
 177       ligature_list[num_ligatures].set (ligature_glyph);
 178       component_count_list[num_ligatures] = 2;
 179       component_list[num_ligatures].set (second_glyph);
 180       num_ligatures++;
 181     }
 182   }
 183 
 184   if (!num_ligatures)
 185     return NULL;
 186 
 187   OT::Supplier<OT::GlyphID>   first_glyphs_supplier                      (first_glyphs, num_first_glyphs);
 188   OT::Supplier<unsigned int > ligature_per_first_glyph_count_supplier    (ligature_per_first_glyph_count_list, num_first_glyphs);
 189   OT::Supplier<OT::GlyphID>   ligatures_supplier                         (ligature_list, num_ligatures);
 190   OT::Supplier<unsigned int > component_count_supplier                   (component_count_list, num_ligatures);
 191   OT::Supplier<OT::GlyphID>   component_supplier                         (component_list, num_ligatures);
 192 
 193   /* 16 bytes per ligature ought to be enough... */
 194   char buf[ARRAY_LENGTH_CONST (ligature_list) * 16 + 128];
 195   OT::hb_serialize_context_t c (buf, sizeof (buf));
 196   OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
 197   bool ret = lookup->serialize_ligature (&c,
 198                                          OT::LookupFlag::IgnoreMarks,
 199                                          first_glyphs_supplier,
 200                                          ligature_per_first_glyph_count_supplier,
 201                                          num_first_glyphs,
 202                                          ligatures_supplier,
 203                                          component_count_supplier,
 204                                          component_supplier);
 205 
 206   c.end_serialize ();
 207   /* TODO sanitize the results? */
 208 
 209   return ret ? c.copy<OT::SubstLookup> () : NULL;
 210 }
 211 
 212 static OT::SubstLookup *
 213 arabic_fallback_synthesize_lookup (const hb_ot_shape_plan_t *plan,
 214                                    hb_font_t *font,
 215                                    unsigned int feature_index)
 216 {
 217   if (feature_index < 4)
 218     return arabic_fallback_synthesize_lookup_single (plan, font, feature_index);
 219   else
 220     return arabic_fallback_synthesize_lookup_ligature (plan, font);
 221 }
 222 
 223 #define ARABIC_FALLBACK_MAX_LOOKUPS 5
 224 
 225 struct arabic_fallback_plan_t
 226 {
 227   ASSERT_POD ();
 228 
 229   unsigned int num_lookups;
 230   bool free_lookups;
 231 
 232   hb_mask_t mask_array[ARABIC_FALLBACK_MAX_LOOKUPS];
 233   OT::SubstLookup *lookup_array[ARABIC_FALLBACK_MAX_LOOKUPS];
 234   hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_FALLBACK_MAX_LOOKUPS];
 235 };
 236 
 237 static const arabic_fallback_plan_t arabic_fallback_plan_nil = {};
 238 
 239 #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_WIN1256)
 240 #define HB_WITH_WIN1256
 241 #endif
 242 
 243 #ifdef HB_WITH_WIN1256
 244 #include "hb-ot-shape-complex-arabic-win1256.hh"
 245 #endif
 246 
 247 struct ManifestLookup {
 248   OT::Tag tag;
 249   OT::OffsetTo<OT::SubstLookup> lookupOffset;
 250 };
 251 typedef OT::ArrayOf<ManifestLookup> Manifest;
 252 
 253 static bool
 254 arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan,
 255                                    const hb_ot_shape_plan_t *plan,
 256                                    hb_font_t *font)
 257 {
 258 #ifdef HB_WITH_WIN1256
 259   /* Does this font look like it's Windows-1256-encoded? */
 260   hb_codepoint_t g;
 261   if (!(hb_font_get_glyph (font, 0x0627u, 0, &g) && g == 199 /* ALEF */ &&
 262         hb_font_get_glyph (font, 0x0644u, 0, &g) && g == 225 /* LAM */ &&
 263         hb_font_get_glyph (font, 0x0649u, 0, &g) && g == 236 /* ALEF MAKSURA */ &&
 264         hb_font_get_glyph (font, 0x064Au, 0, &g) && g == 237 /* YEH */ &&
 265         hb_font_get_glyph (font, 0x0652u, 0, &g) && g == 250 /* SUKUN */))
 266     return false;
 267 
 268   const Manifest &manifest = reinterpret_cast<const Manifest&> (arabic_win1256_gsub_lookups.manifest);
 269   ASSERT_STATIC (sizeof (arabic_win1256_gsub_lookups.manifestData) / sizeof (ManifestLookup)
 270                  <= ARABIC_FALLBACK_MAX_LOOKUPS);
 271   /* TODO sanitize the table? */
 272 
 273   unsigned j = 0;
 274   unsigned int count = manifest.len;
 275   for (unsigned int i = 0; i < count; i++)
 276   {
 277     fallback_plan->mask_array[j] = plan->map.get_1_mask (manifest[i].tag);
 278     if (fallback_plan->mask_array[j])
 279     {
 280       fallback_plan->lookup_array[j] = const_cast<OT::SubstLookup*> (&(&manifest+manifest[i].lookupOffset));
 281       if (fallback_plan->lookup_array[j])
 282       {
 283         fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]);
 284         j++;
 285       }
 286     }
 287   }
 288 
 289   fallback_plan->num_lookups = j;
 290   fallback_plan->free_lookups = false;
 291 
 292   return j > 0;
 293 #else
 294   return false;
 295 #endif
 296 }
 297 
 298 static bool
 299 arabic_fallback_plan_init_unicode (arabic_fallback_plan_t *fallback_plan,
 300                                    const hb_ot_shape_plan_t *plan,
 301                                    hb_font_t *font)
 302 {
 303   ASSERT_STATIC (ARRAY_LENGTH_CONST(arabic_fallback_features) <= ARABIC_FALLBACK_MAX_LOOKUPS);
 304   unsigned int j = 0;
 305   for (unsigned int i = 0; i < ARRAY_LENGTH(arabic_fallback_features) ; i++)
 306   {
 307     fallback_plan->mask_array[j] = plan->map.get_1_mask (arabic_fallback_features[i]);
 308     if (fallback_plan->mask_array[j])
 309     {
 310       fallback_plan->lookup_array[j] = arabic_fallback_synthesize_lookup (plan, font, i);
 311       if (fallback_plan->lookup_array[j])
 312       {
 313         fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]);
 314         j++;
 315       }
 316     }
 317   }
 318 
 319   fallback_plan->num_lookups = j;
 320   fallback_plan->free_lookups = true;
 321 
 322   return j > 0;
 323 }
 324 
 325 static arabic_fallback_plan_t *
 326 arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan,
 327                              hb_font_t *font)
 328 {
 329   arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) calloc (1, sizeof (arabic_fallback_plan_t));
 330   if (unlikely (!fallback_plan))
 331     return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil);
 332 
 333   fallback_plan->num_lookups = 0;
 334   fallback_plan->free_lookups = false;
 335 
 336   /* Try synthesizing GSUB table using Unicode Arabic Presentation Forms,
 337    * in case the font has cmap entries for the presentation-forms characters. */
 338   if (arabic_fallback_plan_init_unicode (fallback_plan, plan, font))
 339     return fallback_plan;
 340 
 341   /* See if this looks like a Windows-1256-encoded font.  If it does, use a
 342    * hand-coded GSUB table. */
 343   if (arabic_fallback_plan_init_win1256 (fallback_plan, plan, font))
 344     return fallback_plan;
 345 
 346   free (fallback_plan);
 347   return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil);
 348 }
 349 
 350 static void
 351 arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan)
 352 {
 353   if (!fallback_plan || fallback_plan == &arabic_fallback_plan_nil)
 354     return;
 355 
 356   for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
 357     if (fallback_plan->lookup_array[i])
 358     {
 359       fallback_plan->accel_array[i].fini ();
 360       if (fallback_plan->free_lookups)
 361         free (fallback_plan->lookup_array[i]);
 362     }
 363 
 364   free (fallback_plan);
 365 }
 366 
 367 static void
 368 arabic_fallback_plan_shape (arabic_fallback_plan_t *fallback_plan,
 369                             hb_font_t *font,
 370                             hb_buffer_t *buffer)
 371 {
 372   OT::hb_apply_context_t c (0, font, buffer);
 373   for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
 374     if (fallback_plan->lookup_array[i]) {
 375       c.set_lookup_mask (fallback_plan->mask_array[i]);
 376       hb_ot_layout_substitute_lookup (&c,
 377                                       *fallback_plan->lookup_array[i],
 378                                       fallback_plan->accel_array[i]);
 379     }
 380 }
 381 
 382 
 383 #endif /* HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH */