]> granicus.if.org Git - libass/blob - libass/ass_shaper.c
Fix missing pointer dereference in 1636a551
[libass] / libass / ass_shaper.c
1 /*
2  * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
3  *
4  * This file is part of libass.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include "config.h"
20
21 #include "ass_shaper.h"
22 #include "ass_render.h"
23 #include "ass_font.h"
24 #include "ass_parse.h"
25 #include "ass_cache.h"
26
27 #define MAX_RUNS 50
28
29 #ifdef CONFIG_HARFBUZZ
30 #include <hb-ft.h>
31 enum {
32     VERT = 0,
33     VKNA,
34     KERN,
35     LIGA,
36     CLIG
37 };
38 #define NUM_FEATURES 5
39 #endif
40
41 struct ass_shaper {
42     ASS_ShapingLevel shaping_level;
43
44     // FriBidi log2vis
45     int n_glyphs;
46     FriBidiChar *event_text;
47     FriBidiCharType *ctypes;
48     FriBidiLevel *emblevels;
49     FriBidiStrIndex *cmap;
50     FriBidiParType base_direction;
51
52 #ifdef CONFIG_HARFBUZZ
53     // OpenType features
54     int n_features;
55     hb_feature_t *features;
56     hb_language_t language;
57
58     // Glyph metrics cache, to speed up shaping
59     Cache *metrics_cache;
60 #endif
61 };
62
63 #ifdef CONFIG_HARFBUZZ
64 struct ass_shaper_metrics_data {
65     Cache *metrics_cache;
66     GlyphMetricsHashKey hash_key;
67     int vertical;
68 };
69
70 struct ass_shaper_font_data {
71     hb_font_t *fonts[ASS_FONT_MAX_FACES];
72     hb_font_funcs_t *font_funcs[ASS_FONT_MAX_FACES];
73     struct ass_shaper_metrics_data *metrics_data[ASS_FONT_MAX_FACES];
74 };
75 #endif
76
77 /**
78  * \brief Print version information
79  */
80 void ass_shaper_info(ASS_Library *lib)
81 {
82     ass_msg(lib, MSGL_V, "Shaper: FriBidi "
83             FRIBIDI_VERSION " (SIMPLE)"
84 #ifdef CONFIG_HARFBUZZ
85             " HarfBuzz-ng %s (COMPLEX)", hb_version_string()
86 #endif
87            );
88 }
89
90 /**
91  * \brief grow arrays, if needed
92  * \param new_size requested size
93  */
94 static void check_allocations(ASS_Shaper *shaper, size_t new_size)
95 {
96     if (new_size > shaper->n_glyphs) {
97         shaper->event_text = realloc(shaper->event_text, sizeof(FriBidiChar) * new_size);
98         shaper->ctypes     = realloc(shaper->ctypes, sizeof(FriBidiCharType) * new_size);
99         shaper->emblevels  = realloc(shaper->emblevels, sizeof(FriBidiLevel) * new_size);
100         shaper->cmap       = realloc(shaper->cmap, sizeof(FriBidiStrIndex) * new_size);
101     }
102 }
103
104 /**
105  * \brief Free shaper and related data
106  */
107 void ass_shaper_free(ASS_Shaper *shaper)
108 {
109 #ifdef CONFIG_HARFBUZZ
110     ass_cache_done(shaper->metrics_cache);
111     free(shaper->features);
112 #endif
113     free(shaper->event_text);
114     free(shaper->ctypes);
115     free(shaper->emblevels);
116     free(shaper->cmap);
117     free(shaper);
118 }
119
120 void ass_shaper_font_data_free(ASS_ShaperFontData *priv)
121 {
122 #ifdef CONFIG_HARFBUZZ
123     int i;
124     for (i = 0; i < ASS_FONT_MAX_FACES; i++)
125         if (priv->fonts[i]) {
126             free(priv->metrics_data[i]);
127             hb_font_destroy(priv->fonts[i]);
128             hb_font_funcs_destroy(priv->font_funcs[i]);
129         }
130     free(priv);
131 #endif
132 }
133
134 #ifdef CONFIG_HARFBUZZ
135 /**
136  * \brief set up the HarfBuzz OpenType feature list with some
137  * standard features.
138  */
139 static void init_features(ASS_Shaper *shaper)
140 {
141     shaper->features = calloc(sizeof(hb_feature_t), NUM_FEATURES);
142
143     shaper->n_features = NUM_FEATURES;
144     shaper->features[VERT].tag = HB_TAG('v', 'e', 'r', 't');
145     shaper->features[VERT].end = INT_MAX;
146     shaper->features[VKNA].tag = HB_TAG('v', 'k', 'n', 'a');
147     shaper->features[VKNA].end = INT_MAX;
148     shaper->features[KERN].tag = HB_TAG('k', 'e', 'r', 'n');
149     shaper->features[KERN].end = INT_MAX;
150     shaper->features[LIGA].tag = HB_TAG('l', 'i', 'g', 'a');
151     shaper->features[LIGA].end = INT_MAX;
152     shaper->features[CLIG].tag = HB_TAG('c', 'l', 'i', 'g');
153     shaper->features[CLIG].end = INT_MAX;
154 }
155
156 /**
157  * \brief Set features depending on properties of the run
158  */
159 static void set_run_features(ASS_Shaper *shaper, GlyphInfo *info)
160 {
161     // enable vertical substitutions for @font runs
162     if (info->font->desc.vertical)
163         shaper->features[VERT].value = shaper->features[VKNA].value = 1;
164     else
165         shaper->features[VERT].value = shaper->features[VKNA].value = 0;
166
167     // disable ligatures if horizontal spacing is non-standard
168     if (info->hspacing)
169         shaper->features[LIGA].value = shaper->features[CLIG].value = 0;
170     else
171         shaper->features[LIGA].value = shaper->features[CLIG].value = 1;
172 }
173
174 /**
175  * \brief Update HarfBuzz's idea of font metrics
176  * \param hb_font HarfBuzz font
177  * \param face associated FreeType font face
178  */
179 static void update_hb_size(hb_font_t *hb_font, FT_Face face)
180 {
181     hb_font_set_scale (hb_font,
182             ((uint64_t) face->size->metrics.x_scale * (uint64_t) face->units_per_EM) >> 16,
183             ((uint64_t) face->size->metrics.y_scale * (uint64_t) face->units_per_EM) >> 16);
184     hb_font_set_ppem (hb_font, face->size->metrics.x_ppem,
185             face->size->metrics.y_ppem);
186 }
187
188
189 /*
190  * Cached glyph metrics getters follow
191  *
192  * These functions replace HarfBuzz' standard FreeType font functions
193  * and provide cached access to essential glyph metrics. This usually
194  * speeds up shaping a lot. It also allows us to use custom load flags.
195  *
196  */
197
198 GlyphMetricsHashValue *
199 get_cached_metrics(struct ass_shaper_metrics_data *metrics, FT_Face face,
200                    hb_codepoint_t unicode, hb_codepoint_t glyph)
201 {
202     GlyphMetricsHashValue *val;
203
204     metrics->hash_key.glyph_index = glyph;
205     val = ass_cache_get(metrics->metrics_cache, &metrics->hash_key);
206
207     if (!val) {
208         int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
209             | FT_LOAD_IGNORE_TRANSFORM;
210         GlyphMetricsHashValue new_val;
211
212         if (FT_Load_Glyph(face, glyph, load_flags))
213             return NULL;
214
215         memcpy(&new_val.metrics, &face->glyph->metrics, sizeof(FT_Glyph_Metrics));
216
217         // if @font rendering is enabled and the glyph should be rotated,
218         // make cached_h_advance pick up the right advance later
219         if (metrics->vertical && unicode >= VERTICAL_LOWER_BOUND)
220             new_val.metrics.horiAdvance = new_val.metrics.vertAdvance;
221
222         val = ass_cache_put(metrics->metrics_cache, &metrics->hash_key, &new_val);
223     }
224
225     return val;
226 }
227
228 static hb_bool_t
229 get_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode,
230           hb_codepoint_t variation, hb_codepoint_t *glyph, void *user_data)
231 {
232     FT_Face face = font_data;
233     struct ass_shaper_metrics_data *metrics_priv = user_data;
234
235     if (variation)
236         *glyph = FT_Face_GetCharVariantIndex(face, ass_font_index_magic(face, unicode), variation);
237     else
238         *glyph = FT_Get_Char_Index(face, ass_font_index_magic(face, unicode));
239
240     // rotate glyph advances for @fonts while we still know the Unicode codepoints
241     if (*glyph != 0)
242         get_cached_metrics(metrics_priv, face, unicode, *glyph);
243
244     return *glyph != 0;
245 }
246
247 static hb_position_t
248 cached_h_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
249                  void *user_data)
250 {
251     FT_Face face = font_data;
252     struct ass_shaper_metrics_data *metrics_priv = user_data;
253     GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
254
255     if (!metrics)
256         return 0;
257
258     return metrics->metrics.horiAdvance;
259 }
260
261 static hb_position_t
262 cached_v_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
263                  void *user_data)
264 {
265     FT_Face face = font_data;
266     struct ass_shaper_metrics_data *metrics_priv = user_data;
267     GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
268
269     if (!metrics)
270         return 0;
271
272     return metrics->metrics.vertAdvance;
273
274 }
275
276 static hb_bool_t
277 cached_h_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
278                 hb_position_t *x, hb_position_t *y, void *user_data)
279 {
280     return 1;
281 }
282
283 static hb_bool_t
284 cached_v_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
285                 hb_position_t *x, hb_position_t *y, void *user_data)
286 {
287     FT_Face face = font_data;
288     struct ass_shaper_metrics_data *metrics_priv = user_data;
289     GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
290
291     if (!metrics)
292         return 0;
293
294     *x = metrics->metrics.horiBearingX - metrics->metrics.vertBearingX;
295     *y = metrics->metrics.horiBearingY - (-metrics->metrics.vertBearingY);
296
297     return 1;
298 }
299
300 static hb_position_t
301 get_h_kerning(hb_font_t *font, void *font_data, hb_codepoint_t first,
302                  hb_codepoint_t second, void *user_data)
303 {
304     FT_Face face = font_data;
305     FT_Vector kern;
306
307     if (FT_Get_Kerning (face, first, second, FT_KERNING_DEFAULT, &kern))
308         return 0;
309
310     return kern.x;
311 }
312
313 static hb_position_t
314 get_v_kerning(hb_font_t *font, void *font_data, hb_codepoint_t first,
315                  hb_codepoint_t second, void *user_data)
316 {
317     return 0;
318 }
319
320 static hb_bool_t
321 cached_extents(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
322                hb_glyph_extents_t *extents, void *user_data)
323 {
324     FT_Face face = font_data;
325     struct ass_shaper_metrics_data *metrics_priv = user_data;
326     GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
327
328     if (!metrics)
329         return 0;
330
331     extents->x_bearing = metrics->metrics.horiBearingX;
332     extents->y_bearing = metrics->metrics.horiBearingY;
333     extents->width     = metrics->metrics.width;
334     extents->height    = metrics->metrics.height;
335
336     return 1;
337 }
338
339 static hb_bool_t
340 get_contour_point(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
341                      unsigned int point_index, hb_position_t *x,
342                      hb_position_t *y, void *user_data)
343 {
344     FT_Face face = font_data;
345     int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
346         | FT_LOAD_IGNORE_TRANSFORM;
347
348     if (FT_Load_Glyph(face, glyph, load_flags))
349         return 0;
350
351     if (point_index >= (unsigned)face->glyph->outline.n_points)
352         return 0;
353
354     *x = face->glyph->outline.points[point_index].x;
355     *y = face->glyph->outline.points[point_index].y;
356
357     return 1;
358 }
359
360 /**
361  * \brief Retrieve HarfBuzz font from cache.
362  * Create it from FreeType font, if needed.
363  * \param info glyph cluster
364  * \return HarfBuzz font
365  */
366 static hb_font_t *get_hb_font(ASS_Shaper *shaper, GlyphInfo *info)
367 {
368     ASS_Font *font = info->font;
369     hb_font_t **hb_fonts;
370
371     if (!font->shaper_priv)
372         font->shaper_priv = calloc(sizeof(ASS_ShaperFontData), 1);
373
374
375     hb_fonts = font->shaper_priv->fonts;
376     if (!hb_fonts[info->face_index]) {
377         hb_fonts[info->face_index] =
378             hb_ft_font_create(font->faces[info->face_index], NULL);
379
380         // set up cached metrics access
381         font->shaper_priv->metrics_data[info->face_index] =
382             calloc(sizeof(struct ass_shaper_metrics_data), 1);
383         struct ass_shaper_metrics_data *metrics =
384             font->shaper_priv->metrics_data[info->face_index];
385         metrics->metrics_cache = shaper->metrics_cache;
386         metrics->vertical = info->font->desc.vertical;
387
388         hb_font_funcs_t *funcs = hb_font_funcs_create();
389         font->shaper_priv->font_funcs[info->face_index] = funcs;
390         hb_font_funcs_set_glyph_func(funcs, get_glyph,
391                 metrics, NULL);
392         hb_font_funcs_set_glyph_h_advance_func(funcs, cached_h_advance,
393                 metrics, NULL);
394         hb_font_funcs_set_glyph_v_advance_func(funcs, cached_v_advance,
395                 metrics, NULL);
396         hb_font_funcs_set_glyph_h_origin_func(funcs, cached_h_origin,
397                 metrics, NULL);
398         hb_font_funcs_set_glyph_v_origin_func(funcs, cached_v_origin,
399                 metrics, NULL);
400         hb_font_funcs_set_glyph_h_kerning_func(funcs, get_h_kerning,
401                 metrics, NULL);
402         hb_font_funcs_set_glyph_v_kerning_func(funcs, get_v_kerning,
403                 metrics, NULL);
404         hb_font_funcs_set_glyph_extents_func(funcs, cached_extents,
405                 metrics, NULL);
406         hb_font_funcs_set_glyph_contour_point_func(funcs, get_contour_point,
407                 metrics, NULL);
408         hb_font_set_funcs(hb_fonts[info->face_index], funcs,
409                 font->faces[info->face_index], NULL);
410     }
411
412     ass_face_set_size(font->faces[info->face_index], info->font_size);
413     update_hb_size(hb_fonts[info->face_index], font->faces[info->face_index]);
414
415     // update hash key for cached metrics
416     struct ass_shaper_metrics_data *metrics =
417         font->shaper_priv->metrics_data[info->face_index];
418     metrics->hash_key.font = info->font;
419     metrics->hash_key.face_index = info->face_index;
420     metrics->hash_key.size = info->font_size;
421     metrics->hash_key.scale_x = double_to_d6(info->scale_x);
422     metrics->hash_key.scale_y = double_to_d6(info->scale_y);
423
424     return hb_fonts[info->face_index];
425 }
426
427 /**
428  * \brief Map script to default language.
429  *
430  * This maps a script to a language, if a script has a representative
431  * language it is typically used with. Otherwise, the invalid language
432  * is returned.
433  *
434  * The mapping is similar to Pango's pango-language.c.
435  *
436  * \param script script tag
437  * \return language tag
438  */
439 static hb_language_t script_to_language(hb_script_t script)
440 {
441     switch (script) {
442         // Unicode 1.1
443         case HB_SCRIPT_ARABIC: return hb_language_from_string("ar", -1); break;
444         case HB_SCRIPT_ARMENIAN: return hb_language_from_string("hy", -1); break;
445         case HB_SCRIPT_BENGALI: return hb_language_from_string("bn", -1); break;
446         case HB_SCRIPT_CANADIAN_ABORIGINAL: return hb_language_from_string("iu", -1); break;
447         case HB_SCRIPT_CHEROKEE: return hb_language_from_string("chr", -1); break;
448         case HB_SCRIPT_COPTIC: return hb_language_from_string("cop", -1); break;
449         case HB_SCRIPT_CYRILLIC: return hb_language_from_string("ru", -1); break;
450         case HB_SCRIPT_DEVANAGARI: return hb_language_from_string("hi", -1); break;
451         case HB_SCRIPT_GEORGIAN: return hb_language_from_string("ka", -1); break;
452         case HB_SCRIPT_GREEK: return hb_language_from_string("el", -1); break;
453         case HB_SCRIPT_GUJARATI: return hb_language_from_string("gu", -1); break;
454         case HB_SCRIPT_GURMUKHI: return hb_language_from_string("pa", -1); break;
455         case HB_SCRIPT_HANGUL: return hb_language_from_string("ko", -1); break;
456         case HB_SCRIPT_HEBREW: return hb_language_from_string("he", -1); break;
457         case HB_SCRIPT_HIRAGANA: return hb_language_from_string("ja", -1); break;
458         case HB_SCRIPT_KANNADA: return hb_language_from_string("kn", -1); break;
459         case HB_SCRIPT_KATAKANA: return hb_language_from_string("ja", -1); break;
460         case HB_SCRIPT_LAO: return hb_language_from_string("lo", -1); break;
461         case HB_SCRIPT_LATIN: return hb_language_from_string("en", -1); break;
462         case HB_SCRIPT_MALAYALAM: return hb_language_from_string("ml", -1); break;
463         case HB_SCRIPT_MONGOLIAN: return hb_language_from_string("mn", -1); break;
464         case HB_SCRIPT_ORIYA: return hb_language_from_string("or", -1); break;
465         case HB_SCRIPT_SYRIAC: return hb_language_from_string("syr", -1); break;
466         case HB_SCRIPT_TAMIL: return hb_language_from_string("ta", -1); break;
467         case HB_SCRIPT_TELUGU: return hb_language_from_string("te", -1); break;
468         case HB_SCRIPT_THAI: return hb_language_from_string("th", -1); break;
469
470         // Unicode 2.0
471         case HB_SCRIPT_TIBETAN: return hb_language_from_string("bo", -1); break;
472
473         // Unicode 3.0
474         case HB_SCRIPT_ETHIOPIC: return hb_language_from_string("am", -1); break;
475         case HB_SCRIPT_KHMER: return hb_language_from_string("km", -1); break;
476         case HB_SCRIPT_MYANMAR: return hb_language_from_string("my", -1); break;
477         case HB_SCRIPT_SINHALA: return hb_language_from_string("si", -1); break;
478         case HB_SCRIPT_THAANA: return hb_language_from_string("dv", -1); break;
479
480         // Unicode 3.2
481         case HB_SCRIPT_BUHID: return hb_language_from_string("bku", -1); break;
482         case HB_SCRIPT_HANUNOO: return hb_language_from_string("hnn", -1); break;
483         case HB_SCRIPT_TAGALOG: return hb_language_from_string("tl", -1); break;
484         case HB_SCRIPT_TAGBANWA: return hb_language_from_string("tbw", -1); break;
485
486         // Unicode 4.0
487         case HB_SCRIPT_UGARITIC: return hb_language_from_string("uga", -1); break;
488
489         // Unicode 4.1
490         case HB_SCRIPT_BUGINESE: return hb_language_from_string("bug", -1); break;
491         case HB_SCRIPT_OLD_PERSIAN: return hb_language_from_string("peo", -1); break;
492         case HB_SCRIPT_SYLOTI_NAGRI: return hb_language_from_string("syl", -1); break;
493
494         // Unicode 5.0
495         case HB_SCRIPT_NKO: return hb_language_from_string("nko", -1); break;
496
497         // no representative language exists
498         default: return HB_LANGUAGE_INVALID; break;
499     }
500 }
501
502 /**
503  * \brief Determine language to be used for shaping a run.
504  *
505  * \param shaper shaper instance
506  * \param script script tag associated with run
507  * \return language tag
508  */
509 static hb_language_t
510 hb_shaper_get_run_language(ASS_Shaper *shaper, hb_script_t script)
511 {
512     hb_language_t lang;
513
514     // override set, use it
515     if (shaper->language != HB_LANGUAGE_INVALID)
516         return shaper->language;
517
518     // get default language for given script
519     lang = script_to_language(script);
520
521     // no dice, use system default
522     if (lang == HB_LANGUAGE_INVALID)
523         lang = hb_language_get_default();
524
525     return lang;
526 }
527
528 /**
529  * \brief Shape event text with HarfBuzz. Full OpenType shaping.
530  * \param glyphs glyph clusters
531  * \param len number of clusters
532  */
533 static void shape_harfbuzz(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len)
534 {
535     int i, j;
536     int run = 0;
537     struct {
538         int offset;
539         int end;
540         hb_buffer_t *buf;
541         hb_font_t *font;
542     } runs[MAX_RUNS];
543
544     for (i = 0; i < len && run < MAX_RUNS; i++, run++) {
545         // get length and level of the current run
546         int k = i;
547         int level = glyphs[i].shape_run_id;
548         int direction = shaper->emblevels[k] % 2;
549         hb_script_t script = glyphs[i].script;
550         while (i < (len - 1) && level == glyphs[i+1].shape_run_id)
551             i++;
552         runs[run].offset = k;
553         runs[run].end    = i;
554         runs[run].buf    = hb_buffer_create();
555         runs[run].font   = get_hb_font(shaper, glyphs + k);
556         set_run_features(shaper, glyphs + k);
557         hb_buffer_pre_allocate(runs[run].buf, i - k + 1);
558         hb_buffer_set_direction(runs[run].buf, direction ? HB_DIRECTION_RTL :
559                 HB_DIRECTION_LTR);
560         hb_buffer_set_language(runs[run].buf,
561                 hb_shaper_get_run_language(shaper, script));
562         hb_buffer_set_script(runs[run].buf, script);
563         hb_buffer_add_utf32(runs[run].buf, shaper->event_text + k, i - k + 1,
564                 0, i - k + 1);
565         hb_shape(runs[run].font, runs[run].buf, shaper->features,
566                 shaper->n_features);
567     }
568
569     // Initialize: skip all glyphs, this is undone later as needed
570     for (i = 0; i < len; i++)
571         glyphs[i].skip = 1;
572
573     // Update glyph indexes, positions and advances from the shaped runs
574     for (i = 0; i < run; i++) {
575         int num_glyphs = hb_buffer_get_length(runs[i].buf);
576         hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(runs[i].buf, NULL);
577         hb_glyph_position_t *pos    = hb_buffer_get_glyph_positions(runs[i].buf, NULL);
578
579         for (j = 0; j < num_glyphs; j++) {
580             int idx = glyph_info[j].cluster + runs[i].offset;
581             GlyphInfo *info = glyphs + idx;
582             GlyphInfo *root = info;
583
584             // if we have more than one glyph per cluster, allocate a new one
585             // and attach to the root glyph
586             if (info->skip == 0) {
587                 while (info->next)
588                     info = info->next;
589                 info->next = malloc(sizeof(GlyphInfo));
590                 memcpy(info->next, info, sizeof(GlyphInfo));
591                 info = info->next;
592                 info->next = NULL;
593             }
594
595             // set position and advance
596             info->skip = 0;
597             info->glyph_index = glyph_info[j].codepoint;
598             info->offset.x    = pos[j].x_offset * info->scale_x;
599             info->offset.y    = -pos[j].y_offset * info->scale_y;
600             info->advance.x   = pos[j].x_advance * info->scale_x;
601             info->advance.y   = -pos[j].y_advance * info->scale_y;
602
603             // accumulate advance in the root glyph
604             root->cluster_advance.x += info->advance.x;
605             root->cluster_advance.y += info->advance.y;
606         }
607     }
608
609     // Free runs and associated data
610     for (i = 0; i < run; i++) {
611         hb_buffer_destroy(runs[i].buf);
612     }
613
614 }
615
616 /**
617  * \brief Determine script property of all characters. Characters of script
618  * common and inherited get their script from their context.
619  *
620  */
621 void ass_shaper_determine_script(ASS_Shaper *shaper, GlyphInfo *glyphs,
622                                   size_t len)
623 {
624     int i;
625     int backwards_scan = 0;
626     hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default();
627     hb_script_t last_script = HB_SCRIPT_UNKNOWN;
628
629     // determine script (forward scan)
630     for (i = 0; i < len; i++) {
631         GlyphInfo *info = glyphs + i;
632         info->script = hb_unicode_script(ufuncs, info->symbol);
633
634         // common/inherit codepoints inherit script from context
635         if (info->script == HB_SCRIPT_COMMON ||
636                 info->script == HB_SCRIPT_INHERITED) {
637             // unknown is not a valid context
638             if (last_script != HB_SCRIPT_UNKNOWN)
639                 info->script = last_script;
640             else
641                 // do a backwards scan to check if next codepoint
642                 // contains a valid script for context
643                 backwards_scan = 1;
644         } else {
645             last_script = info->script;
646         }
647     }
648
649     // determine script (backwards scan, if needed)
650     last_script = HB_SCRIPT_UNKNOWN;
651     for (i = len - 1; i >= 0 && backwards_scan; i--) {
652         GlyphInfo *info = glyphs + i;
653
654         // common/inherit codepoints inherit script from context
655         if (info->script == HB_SCRIPT_COMMON ||
656                 info->script == HB_SCRIPT_INHERITED) {
657             // unknown script is not a valid context
658             if (last_script != HB_SCRIPT_UNKNOWN)
659                 info->script = last_script;
660         } else {
661             last_script = info->script;
662         }
663     }
664 }
665 #endif
666
667 /**
668  * \brief Shape event text with FriBidi. Does mirroring and simple
669  * Arabic shaping.
670  * \param len number of clusters
671  */
672 static void shape_fribidi(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len)
673 {
674     int i;
675     FriBidiJoiningType *joins = calloc(sizeof(*joins), len);
676
677     // shape on codepoint level
678     fribidi_get_joining_types(shaper->event_text, len, joins);
679     fribidi_join_arabic(shaper->ctypes, len, shaper->emblevels, joins);
680     fribidi_shape(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC,
681             shaper->emblevels, len, joins, shaper->event_text);
682
683     // update indexes
684     for (i = 0; i < len; i++) {
685         GlyphInfo *info = glyphs + i;
686         FT_Face face = info->font->faces[info->face_index];
687         info->symbol = shaper->event_text[i];
688         info->glyph_index = FT_Get_Char_Index(face, ass_font_index_magic(face, shaper->event_text[i]));
689     }
690
691     free(joins);
692 }
693
694 /**
695  * \brief Toggle kerning for HarfBuzz shaping.
696  * NOTE: currently only works with OpenType fonts, the TrueType fallback *always*
697  * kerns. It's a bug in HarfBuzz.
698  */
699 void ass_shaper_set_kerning(ASS_Shaper *shaper, int kern)
700 {
701 #ifdef CONFIG_HARFBUZZ
702     shaper->features[KERN].value = !!kern;
703 #endif
704 }
705
706 /**
707  * \brief Find shape runs according to the event's selected fonts
708  */
709 void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv,
710                           GlyphInfo *glyphs, size_t len)
711 {
712     int i;
713     int shape_run = 0;
714
715 #ifdef CONFIG_HARFBUZZ
716     ass_shaper_determine_script(shaper, glyphs, len);
717 #endif
718
719     // find appropriate fonts for the shape runs
720     for (i = 0; i < len; i++) {
721         GlyphInfo *last = glyphs + i - 1;
722         GlyphInfo *info = glyphs + i;
723         // skip drawings
724         if (info->symbol == 0xfffc)
725             continue;
726         // set size and get glyph index
727         ass_font_get_index(render_priv->fontconfig_priv, info->font,
728                 info->symbol, &info->face_index, &info->glyph_index);
729         // shape runs share the same font face and size
730         if (i > 0 && (last->font != info->font ||
731                     last->font_size != info->font_size ||
732                     last->scale_x != info->scale_x ||
733                     last->scale_y != info->scale_y ||
734                     last->hspacing != info->hspacing ||
735                     last->face_index != info->face_index ||
736                     last->script != info->script))
737             shape_run++;
738         info->shape_run_id = shape_run;
739     }
740 }
741
742 /**
743  * \brief Set base direction (paragraph direction) of the text.
744  * \param dir base direction
745  */
746 void ass_shaper_set_base_direction(ASS_Shaper *shaper, FriBidiParType dir)
747 {
748     shaper->base_direction = dir;
749 }
750
751 /**
752  * \brief Set language hint. Some languages have specific character variants,
753  * like Serbian Cyrillic.
754  * \param lang ISO 639-1 two-letter language code
755  */
756 void ass_shaper_set_language(ASS_Shaper *shaper, const char *code)
757 {
758 #ifdef CONFIG_HARFBUZZ
759     hb_language_t lang;
760
761     if (code)
762         lang = hb_language_from_string(code, -1);
763     else
764         lang = HB_LANGUAGE_INVALID;
765
766     shaper->language = lang;
767 #endif
768 }
769
770 /**
771  * Set shaping level. Essentially switches between FriBidi and HarfBuzz.
772  */
773 void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level)
774 {
775     shaper->shaping_level = level;
776 }
777
778 /**
779   * \brief Remove all zero-width invisible characters from the text.
780   * \param text_info text
781   */
782 static void ass_shaper_skip_characters(TextInfo *text_info)
783 {
784     int i;
785     GlyphInfo *glyphs = text_info->glyphs;
786
787     for (i = 0; i < text_info->length; i++) {
788         // Skip direction override control characters
789         if ((glyphs[i].symbol <= 0x202e && glyphs[i].symbol >= 0x202a)
790                 || (glyphs[i].symbol <= 0x200f && glyphs[i].symbol >= 0x200b)
791                 || (glyphs[i].symbol <= 0x2063 && glyphs[i].symbol >= 0x2060)
792                 || glyphs[i].symbol == 0xfeff
793                 || glyphs[i].symbol == 0x00ad
794                 || glyphs[i].symbol == 0x034f) {
795             glyphs[i].symbol = 0;
796             glyphs[i].skip++;
797         }
798     }
799 }
800
801 /**
802  * \brief Shape an event's text. Calculates directional runs and shapes them.
803  * \param text_info event's text
804  */
805 void ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info)
806 {
807     int i, last_break;
808     FriBidiParType dir;
809     GlyphInfo *glyphs = text_info->glyphs;
810
811     check_allocations(shaper, text_info->length);
812
813     // Get bidi character types and embedding levels
814     last_break = 0;
815     for (i = 0; i < text_info->length; i++) {
816         shaper->event_text[i] = glyphs[i].symbol;
817         // embedding levels should be calculated paragraph by paragraph
818         if (glyphs[i].symbol == '\n' || i == text_info->length - 1) {
819             dir = shaper->base_direction;
820             fribidi_get_bidi_types(shaper->event_text + last_break,
821                     i - last_break + 1, shaper->ctypes + last_break);
822             fribidi_get_par_embedding_levels(shaper->ctypes + last_break,
823                     i - last_break + 1, &dir, shaper->emblevels + last_break);
824             last_break = i + 1;
825         }
826     }
827
828     // add embedding levels to shape runs for final runs
829     for (i = 0; i < text_info->length; i++) {
830         glyphs[i].shape_run_id += shaper->emblevels[i];
831     }
832
833 #ifdef CONFIG_HARFBUZZ
834     switch (shaper->shaping_level) {
835     case ASS_SHAPING_SIMPLE:
836         shape_fribidi(shaper, glyphs, text_info->length);
837         ass_shaper_skip_characters(text_info);
838         break;
839     case ASS_SHAPING_COMPLEX:
840         shape_harfbuzz(shaper, glyphs, text_info->length);
841         break;
842     }
843 #else
844         shape_fribidi(shaper, glyphs, text_info->length);
845         ass_shaper_skip_characters(text_info);
846 #endif
847 }
848
849 /**
850  * \brief Create a new shaper instance and preallocate data structures
851  * \param prealloc preallocation size
852  */
853 ASS_Shaper *ass_shaper_new(size_t prealloc)
854 {
855     ASS_Shaper *shaper = calloc(sizeof(*shaper), 1);
856
857     shaper->base_direction = FRIBIDI_PAR_ON;
858     check_allocations(shaper, prealloc);
859
860 #ifdef CONFIG_HARFBUZZ
861     init_features(shaper);
862     shaper->metrics_cache = ass_glyph_metrics_cache_create();
863 #endif
864
865     return shaper;
866 }
867
868
869 /**
870  * \brief clean up additional data temporarily needed for shaping and
871  * (e.g. additional glyphs allocated)
872  */
873 void ass_shaper_cleanup(ASS_Shaper *shaper, TextInfo *text_info)
874 {
875     int i;
876
877     for (i = 0; i < text_info->length; i++) {
878         GlyphInfo *info = text_info->glyphs + i;
879         info = info->next;
880         while (info) {
881             GlyphInfo *next = info->next;
882             free(info);
883             info = next;
884         }
885     }
886 }
887
888 /**
889  * \brief Calculate reorder map to render glyphs in visual order
890  */
891 FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info)
892 {
893     int i;
894
895     // Initialize reorder map
896     for (i = 0; i < text_info->length; i++)
897         shaper->cmap[i] = i;
898
899     // Create reorder map line-by-line
900     for (i = 0; i < text_info->n_lines; i++) {
901         LineInfo *line = text_info->lines + i;
902         FriBidiParType dir = FRIBIDI_PAR_ON;
903
904         fribidi_reorder_line(0,
905                 shaper->ctypes + line->offset, line->len, 0, dir,
906                 shaper->emblevels + line->offset, NULL,
907                 shaper->cmap + line->offset);
908     }
909
910     return shaper->cmap;
911 }
912
913 /**
914  * \brief Resolve a Windows font charset number to a suitable
915  * base direction. 177 and 178 are Hebrew and Arabic respectively, and
916  * they map to RTL. Everything else maps to LTR for compatibility
917  * reasons. The special value -1, which is not a legal Windows font charset
918  * number, can be used for autodetection.
919  * \param enc Windows font encoding
920  */
921 FriBidiParType resolve_base_direction(int enc)
922 {
923     switch (enc) {
924         case -1:
925             return FRIBIDI_PAR_ON;
926         case 177:
927         case 178:
928             return FRIBIDI_PAR_RTL;
929         default:
930             return FRIBIDI_PAR_LTR;
931     }
932 }