2 * Copyright © 2012 Google, Inc.
4 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
24 * Google Author(s): Behdad Esfahbod
27 #ifndef HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
28 #define HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
30 #include "hb-private.hh"
32 #include "hb-ot-shape-private.hh"
33 #include "hb-ot-layout-gsub-table.hh"
36 /* Features ordered the same as the entries in shaping_table rows,
37 * followed by rlig. Don't change. */
38 static const hb_tag_t arabic_fallback_features
[] =
40 HB_TAG('i','n','i','t'),
41 HB_TAG('m','e','d','i'),
42 HB_TAG('f','i','n','a'),
43 HB_TAG('i','s','o','l'),
44 HB_TAG('r','l','i','g'),
47 static OT::SubstLookup
*
48 arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t
*plan HB_UNUSED
,
50 unsigned int feature_index
)
52 OT::GlyphID glyphs
[SHAPING_TABLE_LAST
- SHAPING_TABLE_FIRST
+ 1];
53 OT::GlyphID substitutes
[SHAPING_TABLE_LAST
- SHAPING_TABLE_FIRST
+ 1];
54 unsigned int num_glyphs
= 0;
57 for (hb_codepoint_t u
= SHAPING_TABLE_FIRST
; u
< SHAPING_TABLE_LAST
+ 1; u
++)
59 hb_codepoint_t s
= shaping_table
[u
- SHAPING_TABLE_FIRST
][feature_index
];
60 hb_codepoint_t u_glyph
, s_glyph
;
63 !hb_font_get_glyph (font
, u
, 0, &u_glyph
) ||
64 !hb_font_get_glyph (font
, s
, 0, &s_glyph
) ||
66 u_glyph
> 0xFFFFu
|| s_glyph
> 0xFFFFu
)
69 glyphs
[num_glyphs
].set (u_glyph
);
70 substitutes
[num_glyphs
].set (s_glyph
);
79 * May not be good-enough for presidential candidate interviews, but good-enough for us... */
80 hb_bubble_sort (&glyphs
[0], num_glyphs
, OT::GlyphID::cmp
, &substitutes
[0]);
82 OT::Supplier
<OT::GlyphID
> glyphs_supplier (glyphs
, num_glyphs
);
83 OT::Supplier
<OT::GlyphID
> substitutes_supplier (substitutes
, num_glyphs
);
85 /* Each glyph takes four bytes max, and there's some overhead. */
86 char buf
[(SHAPING_TABLE_LAST
- SHAPING_TABLE_FIRST
+ 1) * 4 + 128];
87 OT::hb_serialize_context_t
c (buf
, sizeof (buf
));
88 OT::SubstLookup
*lookup
= c
.start_serialize
<OT::SubstLookup
> ();
89 bool ret
= lookup
->serialize_single (&c
,
90 OT::LookupFlag::IgnoreMarks
,
95 /* TODO sanitize the results? */
97 return ret
? c
.copy
<OT::SubstLookup
> () : NULL
;
100 static OT::SubstLookup
*
101 arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t
*plan HB_UNUSED
,
104 OT::GlyphID first_glyphs
[ARRAY_LENGTH_CONST (ligature_table
)];
105 unsigned int first_glyphs_indirection
[ARRAY_LENGTH_CONST (ligature_table
)];
106 unsigned int ligature_per_first_glyph_count_list
[ARRAY_LENGTH_CONST (first_glyphs
)];
107 unsigned int num_first_glyphs
= 0;
109 /* We know that all our ligatures are 2-component */
110 OT::GlyphID ligature_list
[ARRAY_LENGTH_CONST (first_glyphs
) * ARRAY_LENGTH_CONST(ligature_table
[0].ligatures
)];
111 unsigned int component_count_list
[ARRAY_LENGTH_CONST (ligature_list
)];
112 OT::GlyphID component_list
[ARRAY_LENGTH_CONST (ligature_list
) * 1/* One extra component per ligature */];
113 unsigned int num_ligatures
= 0;
115 /* Populate arrays */
117 /* Sort out the first-glyphs */
118 for (unsigned int first_glyph_idx
= 0; first_glyph_idx
< ARRAY_LENGTH (first_glyphs
); first_glyph_idx
++)
120 hb_codepoint_t first_u
= ligature_table
[first_glyph_idx
].first
;
121 hb_codepoint_t first_glyph
;
122 if (!hb_font_get_glyph (font
, first_u
, 0, &first_glyph
))
124 first_glyphs
[num_first_glyphs
].set (first_glyph
);
125 ligature_per_first_glyph_count_list
[num_first_glyphs
] = 0;
126 first_glyphs_indirection
[num_first_glyphs
] = first_glyph_idx
;
129 hb_bubble_sort (&first_glyphs
[0], num_first_glyphs
, OT::GlyphID::cmp
, &first_glyphs_indirection
[0]);
131 /* Now that the first-glyphs are sorted, walk again, populate ligatures. */
132 for (unsigned int i
= 0; i
< num_first_glyphs
; i
++)
134 unsigned int first_glyph_idx
= first_glyphs_indirection
[i
];
136 for (unsigned int second_glyph_idx
= 0; second_glyph_idx
< ARRAY_LENGTH (ligature_table
[0].ligatures
); second_glyph_idx
++)
138 hb_codepoint_t second_u
= ligature_table
[first_glyph_idx
].ligatures
[second_glyph_idx
].second
;
139 hb_codepoint_t ligature_u
= ligature_table
[first_glyph_idx
].ligatures
[second_glyph_idx
].ligature
;
140 hb_codepoint_t second_glyph
, ligature_glyph
;
142 !hb_font_get_glyph (font
, second_u
, 0, &second_glyph
) ||
143 !hb_font_get_glyph (font
, ligature_u
, 0, &ligature_glyph
))
146 ligature_per_first_glyph_count_list
[i
]++;
148 ligature_list
[num_ligatures
].set (ligature_glyph
);
149 component_count_list
[num_ligatures
] = 2;
150 component_list
[num_ligatures
].set (second_glyph
);
158 OT::Supplier
<OT::GlyphID
> first_glyphs_supplier (first_glyphs
, num_first_glyphs
);
159 OT::Supplier
<unsigned int > ligature_per_first_glyph_count_supplier (ligature_per_first_glyph_count_list
, num_first_glyphs
);
160 OT::Supplier
<OT::GlyphID
> ligatures_supplier (ligature_list
, num_ligatures
);
161 OT::Supplier
<unsigned int > component_count_supplier (component_count_list
, num_ligatures
);
162 OT::Supplier
<OT::GlyphID
> component_supplier (component_list
, num_ligatures
);
164 /* 16 bytes per ligature ought to be enough... */
165 char buf
[ARRAY_LENGTH_CONST (ligature_list
) * 16 + 128];
166 OT::hb_serialize_context_t
c (buf
, sizeof (buf
));
167 OT::SubstLookup
*lookup
= c
.start_serialize
<OT::SubstLookup
> ();
168 bool ret
= lookup
->serialize_ligature (&c
,
169 OT::LookupFlag::IgnoreMarks
,
170 first_glyphs_supplier
,
171 ligature_per_first_glyph_count_supplier
,
174 component_count_supplier
,
178 /* TODO sanitize the results? */
180 return ret
? c
.copy
<OT::SubstLookup
> () : NULL
;
183 static OT::SubstLookup
*
184 arabic_fallback_synthesize_lookup (const hb_ot_shape_plan_t
*plan
,
186 unsigned int feature_index
)
188 if (feature_index
< 4)
189 return arabic_fallback_synthesize_lookup_single (plan
, font
, feature_index
);
191 return arabic_fallback_synthesize_lookup_ligature (plan
, font
);
194 #define ARABIC_FALLBACK_MAX_LOOKUPS 5
196 struct arabic_fallback_plan_t
200 unsigned int num_lookups
;
203 hb_mask_t mask_array
[ARABIC_FALLBACK_MAX_LOOKUPS
];
204 OT::SubstLookup
*lookup_array
[ARABIC_FALLBACK_MAX_LOOKUPS
];
205 hb_ot_layout_lookup_accelerator_t accel_array
[ARABIC_FALLBACK_MAX_LOOKUPS
];
208 static const arabic_fallback_plan_t arabic_fallback_plan_nil
= {};
210 #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_WIN1256)
211 #define HB_WITH_WIN1256
214 #ifdef HB_WITH_WIN1256
215 #include "hb-ot-shape-complex-arabic-win1256.hh"
218 struct ManifestLookup
{
220 OT::OffsetTo
<OT::SubstLookup
> lookupOffset
;
222 typedef OT::ArrayOf
<ManifestLookup
> Manifest
;
225 arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t
*fallback_plan
,
226 const hb_ot_shape_plan_t
*plan
,
229 #ifdef HB_WITH_WIN1256
230 /* Does this font look like it's Windows-1256-encoded? */
232 if (!(hb_font_get_glyph (font
, 0x0627u
, 0, &g
) && g
== 199 /* ALEF */ &&
233 hb_font_get_glyph (font
, 0x0644u
, 0, &g
) && g
== 225 /* LAM */ &&
234 hb_font_get_glyph (font
, 0x0649u
, 0, &g
) && g
== 236 /* ALEF MAKSURA */ &&
235 hb_font_get_glyph (font
, 0x064Au
, 0, &g
) && g
== 237 /* YEH */ &&
236 hb_font_get_glyph (font
, 0x0652u
, 0, &g
) && g
== 250 /* SUKUN */))
239 const Manifest
&manifest
= reinterpret_cast<const Manifest
&> (arabic_win1256_gsub_lookups
.manifest
);
240 ASSERT_STATIC (sizeof (arabic_win1256_gsub_lookups
.manifestData
) / sizeof (ManifestLookup
)
241 <= ARABIC_FALLBACK_MAX_LOOKUPS
);
242 /* TODO sanitize the table? */
245 unsigned int count
= manifest
.len
;
246 for (unsigned int i
= 0; i
< count
; i
++)
248 fallback_plan
->mask_array
[j
] = plan
->map
.get_1_mask (manifest
[i
].tag
);
249 if (fallback_plan
->mask_array
[j
])
251 fallback_plan
->lookup_array
[j
] = const_cast<OT::SubstLookup
*> (&(&manifest
+manifest
[i
].lookupOffset
));
252 if (fallback_plan
->lookup_array
[j
])
254 fallback_plan
->accel_array
[j
].init (*fallback_plan
->lookup_array
[j
]);
260 fallback_plan
->num_lookups
= j
;
261 fallback_plan
->free_lookups
= false;
270 arabic_fallback_plan_init_unicode (arabic_fallback_plan_t
*fallback_plan
,
271 const hb_ot_shape_plan_t
*plan
,
274 ASSERT_STATIC (ARRAY_LENGTH_CONST(arabic_fallback_features
) <= ARABIC_FALLBACK_MAX_LOOKUPS
);
276 for (unsigned int i
= 0; i
< ARRAY_LENGTH(arabic_fallback_features
) ; i
++)
278 fallback_plan
->mask_array
[j
] = plan
->map
.get_1_mask (arabic_fallback_features
[i
]);
279 if (fallback_plan
->mask_array
[j
])
281 fallback_plan
->lookup_array
[j
] = arabic_fallback_synthesize_lookup (plan
, font
, i
);
282 if (fallback_plan
->lookup_array
[j
])
284 fallback_plan
->accel_array
[j
].init (*fallback_plan
->lookup_array
[j
]);
290 fallback_plan
->num_lookups
= j
;
291 fallback_plan
->free_lookups
= true;
296 static arabic_fallback_plan_t
*
297 arabic_fallback_plan_create (const hb_ot_shape_plan_t
*plan
,
300 arabic_fallback_plan_t
*fallback_plan
= (arabic_fallback_plan_t
*) calloc (1, sizeof (arabic_fallback_plan_t
));
301 if (unlikely (!fallback_plan
))
302 return const_cast<arabic_fallback_plan_t
*> (&arabic_fallback_plan_nil
);
304 fallback_plan
->num_lookups
= 0;
305 fallback_plan
->free_lookups
= false;
307 /* Try synthesizing GSUB table using Unicode Arabic Presentation Forms,
308 * in case the font has cmap entries for the presentation-forms characters. */
309 if (arabic_fallback_plan_init_unicode (fallback_plan
, plan
, font
))
310 return fallback_plan
;
312 /* See if this looks like a Windows-1256-encoded font. If it does, use a
313 * hand-coded GSUB table. */
314 if (arabic_fallback_plan_init_win1256 (fallback_plan
, plan
, font
))
315 return fallback_plan
;
317 free (fallback_plan
);
318 return const_cast<arabic_fallback_plan_t
*> (&arabic_fallback_plan_nil
);
322 arabic_fallback_plan_destroy (arabic_fallback_plan_t
*fallback_plan
)
324 if (!fallback_plan
|| fallback_plan
== &arabic_fallback_plan_nil
)
327 for (unsigned int i
= 0; i
< fallback_plan
->num_lookups
; i
++)
328 if (fallback_plan
->lookup_array
[i
])
330 fallback_plan
->accel_array
[i
].fini (fallback_plan
->lookup_array
[i
]);
331 if (fallback_plan
->free_lookups
)
332 free (fallback_plan
->lookup_array
[i
]);
335 free (fallback_plan
);
339 arabic_fallback_plan_shape (arabic_fallback_plan_t
*fallback_plan
,
343 OT::hb_apply_context_t
c (0, font
, buffer
);
344 for (unsigned int i
= 0; i
< fallback_plan
->num_lookups
; i
++)
345 if (fallback_plan
->lookup_array
[i
]) {
346 c
.set_lookup_mask (fallback_plan
->mask_array
[i
]);
347 hb_ot_layout_substitute_lookup (&c
,
348 *fallback_plan
->lookup_array
[i
],
349 fallback_plan
->accel_array
[i
]);
354 #endif /* HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH */