OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright © 2012 Google, Inc. |
| 3 * |
| 4 * This is part of HarfBuzz, a text shaping library. |
| 5 * |
| 6 * Permission is hereby granted, without written agreement and without |
| 7 * license or royalty fees, to use, copy, modify, and distribute this |
| 8 * software and its documentation for any purpose, provided that the |
| 9 * above copyright notice and the following two paragraphs appear in |
| 10 * all copies of this software. |
| 11 * |
| 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
| 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
| 14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
| 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
| 16 * DAMAGE. |
| 17 * |
| 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
| 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
| 20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
| 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
| 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
| 23 * |
| 24 * Google Author(s): Behdad Esfahbod |
| 25 */ |
| 26 |
| 27 #ifndef HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH |
| 28 #define HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH |
| 29 |
| 30 #include "hb-private.hh" |
| 31 |
| 32 #include "hb-ot-shape-private.hh" |
| 33 #include "hb-ot-layout-gsub-table.hh" |
| 34 |
| 35 |
| 36 static const hb_tag_t arabic_fallback_features[] = |
| 37 { |
| 38 HB_TAG('i','n','i','t'), |
| 39 HB_TAG('m','e','d','i'), |
| 40 HB_TAG('f','i','n','a'), |
| 41 HB_TAG('i','s','o','l'), |
| 42 HB_TAG('r','l','i','g'), |
| 43 }; |
| 44 |
| 45 /* Same order as the fallback feature array */ |
| 46 enum { |
| 47 FALLBACK_INIT, |
| 48 FALLBACK_MEDI, |
| 49 FALLBACK_FINA, |
| 50 FALLBACK_ISOL, |
| 51 FALLBACK_RLIG, |
| 52 ARABIC_NUM_FALLBACK_FEATURES |
| 53 }; |
| 54 |
| 55 static OT::SubstLookup * |
| 56 arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan, |
| 57 hb_font_t *font, |
| 58 unsigned int feature_index) |
| 59 { |
| 60 OT::GlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; |
| 61 OT::GlyphID substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; |
| 62 unsigned int num_glyphs = 0; |
| 63 |
| 64 /* Populate arrays */ |
| 65 for (hb_codepoint_t u = SHAPING_TABLE_FIRST; u < SHAPING_TABLE_LAST + 1; u++) |
| 66 { |
| 67 hb_codepoint_t s = shaping_table[u - SHAPING_TABLE_FIRST][feature_index]; |
| 68 hb_codepoint_t u_glyph, s_glyph; |
| 69 |
| 70 if (!s || |
| 71 !hb_font_get_glyph (font, u, 0, &u_glyph) || |
| 72 !hb_font_get_glyph (font, s, 0, &s_glyph) || |
| 73 u_glyph == s_glyph || |
| 74 u_glyph > 0xFFFF || s_glyph > 0xFFFF) |
| 75 continue; |
| 76 |
| 77 glyphs[num_glyphs].set (u_glyph); |
| 78 substitutes[num_glyphs].set (s_glyph); |
| 79 |
| 80 num_glyphs++; |
| 81 } |
| 82 |
| 83 /* Bubble-sort! |
| 84 * May not be good-enough for presidential candidate interviews, but good-enou
gh for us... */ |
| 85 hb_bubble_sort (&glyphs[0], num_glyphs, OT::GlyphID::cmp, &substitutes[0]); |
| 86 |
| 87 OT::Supplier<OT::GlyphID> glyphs_supplier (glyphs, num_glyphs); |
| 88 OT::Supplier<OT::GlyphID> substitutes_supplier (substitutes, num_glyphs); |
| 89 |
| 90 /* Each glyph takes four bytes max, and there's some overhead. */ |
| 91 char buf[(SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1) * 4 + 128]; |
| 92 OT::hb_serialize_context_t c (buf, sizeof (buf)); |
| 93 OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> (); |
| 94 bool ret = lookup->serialize_single (&c, |
| 95 OT::LookupFlag::IgnoreMarks, |
| 96 glyphs_supplier, |
| 97 substitutes_supplier, |
| 98 num_glyphs); |
| 99 c.end_serialize (); |
| 100 /* TODO sanitize the results? */ |
| 101 |
| 102 return ret ? c.copy<OT::SubstLookup> () : NULL; |
| 103 } |
| 104 |
| 105 static OT::SubstLookup * |
| 106 arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan, |
| 107 hb_font_t *font) |
| 108 { |
| 109 OT::GlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)]; |
| 110 unsigned int first_glyphs_indirection[ARRAY_LENGTH_CONST (ligature_table)]; |
| 111 unsigned int ligature_per_first_glyph_count_list[ARRAY_LENGTH_CONST (first_gly
phs)]; |
| 112 unsigned int num_first_glyphs = 0; |
| 113 |
| 114 /* We know that all our ligatures are 2-component */ |
| 115 OT::GlyphID ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CON
ST(ligature_table[0].ligatures)]; |
| 116 unsigned int component_count_list[ARRAY_LENGTH_CONST (ligature_list)]; |
| 117 OT::GlyphID component_list[ARRAY_LENGTH_CONST (ligature_list) * 1/* One extra
component per ligature */]; |
| 118 unsigned int num_ligatures = 0; |
| 119 |
| 120 /* Populate arrays */ |
| 121 |
| 122 /* Sort out the first-glyphs */ |
| 123 for (unsigned int first_glyph_idx = 0; first_glyph_idx < ARRAY_LENGTH (first_g
lyphs); first_glyph_idx++) |
| 124 { |
| 125 hb_codepoint_t first_u = ligature_table[first_glyph_idx].first; |
| 126 hb_codepoint_t first_glyph; |
| 127 if (!hb_font_get_glyph (font, first_u, 0, &first_glyph)) |
| 128 continue; |
| 129 first_glyphs[num_first_glyphs].set (first_glyph); |
| 130 ligature_per_first_glyph_count_list[num_first_glyphs] = 0; |
| 131 first_glyphs_indirection[num_first_glyphs] = first_glyph_idx; |
| 132 num_first_glyphs++; |
| 133 } |
| 134 hb_bubble_sort (&first_glyphs[0], num_first_glyphs, OT::GlyphID::cmp, &first_g
lyphs_indirection[0]); |
| 135 |
| 136 /* Now that the first-glyphs are sorted, walk again, populate ligatures. */ |
| 137 for (unsigned int i = 0; i < num_first_glyphs; i++) |
| 138 { |
| 139 unsigned int first_glyph_idx = first_glyphs_indirection[i]; |
| 140 |
| 141 for (unsigned int second_glyph_idx = 0; second_glyph_idx < ARRAY_LENGTH (lig
ature_table[0].ligatures); second_glyph_idx++) |
| 142 { |
| 143 hb_codepoint_t second_u = ligature_table[first_glyph_idx].ligatures[seco
nd_glyph_idx].second; |
| 144 hb_codepoint_t ligature_u = ligature_table[first_glyph_idx].ligatures[seco
nd_glyph_idx].ligature; |
| 145 hb_codepoint_t second_glyph, ligature_glyph; |
| 146 if (!second_u || |
| 147 !hb_font_get_glyph (font, second_u, 0, &second_glyph) || |
| 148 !hb_font_get_glyph (font, ligature_u, 0, &ligature_glyph)) |
| 149 continue; |
| 150 |
| 151 ligature_per_first_glyph_count_list[i]++; |
| 152 |
| 153 ligature_list[num_ligatures].set (ligature_glyph); |
| 154 component_count_list[num_ligatures] = 2; |
| 155 component_list[num_ligatures].set (second_glyph); |
| 156 num_ligatures++; |
| 157 } |
| 158 } |
| 159 |
| 160 OT::Supplier<OT::GlyphID> first_glyphs_supplier (first_
glyphs, num_first_glyphs); |
| 161 OT::Supplier<unsigned int > ligature_per_first_glyph_count_supplier (ligatu
re_per_first_glyph_count_list, num_first_glyphs); |
| 162 OT::Supplier<OT::GlyphID> ligatures_supplier (ligatu
re_list, num_ligatures); |
| 163 OT::Supplier<unsigned int > component_count_supplier (compon
ent_count_list, num_ligatures); |
| 164 OT::Supplier<OT::GlyphID> component_supplier (compon
ent_list, num_ligatures); |
| 165 |
| 166 /* 16 bytes per ligature ought to be enough... */ |
| 167 char buf[ARRAY_LENGTH_CONST (ligature_list) * 16 + 128]; |
| 168 OT::hb_serialize_context_t c (buf, sizeof (buf)); |
| 169 OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> (); |
| 170 bool ret = lookup->serialize_ligature (&c, |
| 171 OT::LookupFlag::IgnoreMarks, |
| 172 first_glyphs_supplier, |
| 173 ligature_per_first_glyph_count_supplier
, |
| 174 num_first_glyphs, |
| 175 ligatures_supplier, |
| 176 component_count_supplier, |
| 177 component_supplier); |
| 178 |
| 179 c.end_serialize (); |
| 180 /* TODO sanitize the results? */ |
| 181 |
| 182 return ret ? c.copy<OT::SubstLookup> () : NULL; |
| 183 } |
| 184 |
| 185 static OT::SubstLookup * |
| 186 arabic_fallback_synthesize_lookup (const hb_ot_shape_plan_t *plan, |
| 187 hb_font_t *font, |
| 188 unsigned int feature_index) |
| 189 { |
| 190 if (feature_index < 4) |
| 191 return arabic_fallback_synthesize_lookup_single (plan, font, feature_index); |
| 192 else |
| 193 return arabic_fallback_synthesize_lookup_ligature (plan, font); |
| 194 } |
| 195 |
| 196 struct arabic_fallback_plan_t |
| 197 { |
| 198 ASSERT_POD (); |
| 199 |
| 200 hb_mask_t mask_array[ARABIC_NUM_FALLBACK_FEATURES]; |
| 201 OT::SubstLookup *lookup_array[ARABIC_NUM_FALLBACK_FEATURES]; |
| 202 hb_set_digest_t digest_array[ARABIC_NUM_FALLBACK_FEATURES]; |
| 203 }; |
| 204 |
| 205 static const arabic_fallback_plan_t arabic_fallback_plan_nil = {}; |
| 206 |
| 207 static arabic_fallback_plan_t * |
| 208 arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, |
| 209 hb_font_t *font) |
| 210 { |
| 211 arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) calloc (1,
sizeof (arabic_fallback_plan_t)); |
| 212 if (unlikely (!fallback_plan)) |
| 213 return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil); |
| 214 |
| 215 for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++) { |
| 216 fallback_plan->mask_array[i] = plan->map.get_1_mask (arabic_fallback_feature
s[i]); |
| 217 if (fallback_plan->mask_array[i]) { |
| 218 fallback_plan->lookup_array[i] = arabic_fallback_synthesize_lookup (plan,
font, i); |
| 219 if (fallback_plan->lookup_array[i]) |
| 220 fallback_plan->lookup_array[i]->add_coverage (&fallback_plan->digest_arr
ay[i]); |
| 221 } |
| 222 } |
| 223 |
| 224 return fallback_plan; |
| 225 } |
| 226 |
| 227 static void |
| 228 arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan) |
| 229 { |
| 230 if (!fallback_plan || fallback_plan == &arabic_fallback_plan_nil) |
| 231 return; |
| 232 |
| 233 for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++) |
| 234 if (fallback_plan->lookup_array[i]) |
| 235 free (fallback_plan->lookup_array[i]); |
| 236 |
| 237 free (fallback_plan); |
| 238 } |
| 239 |
| 240 static void |
| 241 arabic_fallback_plan_shape (arabic_fallback_plan_t *fallback_plan, |
| 242 hb_font_t *font, |
| 243 hb_buffer_t *buffer) |
| 244 { |
| 245 for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++) |
| 246 if (fallback_plan->lookup_array[i]) { |
| 247 OT::hb_apply_context_t c (font, buffer, fallback_plan->mask_array[i]); |
| 248 fallback_plan->lookup_array[i]->apply_string (&c, &fallback_plan->digest_a
rray[i]); |
| 249 } |
| 250 } |
| 251 |
| 252 |
| 253 #endif /* HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH */ |
OLD | NEW |