Commit message: shaper: fix reallocation

Update the variable that tracks the allocated size. This potentially
improves performance and avoid some side effects, which lead to
undefined behavior in some cases.

Fixes fuzzer test case id:000051,sig:11,sync:fuzzer3,src:004221.
CVE: CVE-2016-7972
CWE: CWE-399
Vulnerability description: The check_allocations function in libass/ass_shaper.c in libass before 0.13.4 allows remote attackers to cause a denial of service (memory allocation failure) via unspecified vectors.

Project: libass
File: ass_shaper.c

void ass_shaper_cleanup(ASS_Shaper *shaper, TextInfo *text_info)
{
    int i;

    for (i = 0; i < text_info->length; i++) {
        GlyphInfo *info = text_info->glyphs + i;
        info = info->next;
        while (info) {
            GlyphInfo *next = info->next;
            free(info);
            info = next;
        }
    }
}

Project: libass

void ass_shaper_determine_script(ASS_Shaper *shaper, GlyphInfo *glyphs,
                                  size_t len)
{
    int i;
    int backwards_scan = 0;
    hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default();
    hb_script_t last_script = HB_SCRIPT_UNKNOWN;

    for (i = 0; i < len; i++) {
        GlyphInfo *info = glyphs + i;
        info->script = hb_unicode_script(ufuncs, info->symbol);

        if (info->script == HB_SCRIPT_COMMON ||
                info->script == HB_SCRIPT_INHERITED) {
            if (last_script != HB_SCRIPT_UNKNOWN)
                info->script = last_script;
            else
                backwards_scan = 1;
        } else {
            last_script = info->script;
        }
    }

    last_script = HB_SCRIPT_UNKNOWN;
    for (i = len - 1; i >= 0 && backwards_scan; i--) {
        GlyphInfo *info = glyphs + i;

        if (info->script == HB_SCRIPT_COMMON ||
                info->script == HB_SCRIPT_INHERITED) {
            if (last_script != HB_SCRIPT_UNKNOWN)
                info->script = last_script;
        } else {
            last_script = info->script;
        }
    }
}

Project: libass

void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv,
                          GlyphInfo *glyphs, size_t len)
{
    int i;
    int shape_run = 0;

#ifdef CONFIG_HARFBUZZ
    ass_shaper_determine_script(shaper, glyphs, len);
#endif

    for (i = 0; i < len; i++) {
        GlyphInfo *last = glyphs + i - 1;
        GlyphInfo *info = glyphs + i;
        if (info->symbol == 0xfffc)
            continue;
        ass_font_get_index(render_priv->fontselect, info->font,
                info->symbol, &info->face_index, &info->glyph_index);
        if (i > 0 && (last->font != info->font ||
                    last->face_index != info->face_index ||
                    last->script != info->script ||
                    last->font_size != info->font_size ||
                    last->c[0] != info->c[0] ||
                    last->c[1] != info->c[1] ||
                    last->c[2] != info->c[2] ||
                    last->c[3] != info->c[3] ||
                    last->be != info->be ||
                    last->blur != info->blur ||
                    last->shadow_x != info->shadow_x ||
                    last->shadow_y != info->shadow_y ||
                    last->frx != info->frx ||
                    last->fry != info->fry ||
                    last->frz != info->frz ||
                    last->fax != info->fax ||
                    last->fay != info->fay ||
                    last->scale_x != info->scale_x ||
                    last->scale_y != info->scale_y ||
                    last->border_style != info->border_style ||
                    last->border_x != info->border_x ||
                    last->border_y != info->border_y ||
                    last->hspacing != info->hspacing ||
                    last->italic != info->italic ||
                    last->bold != info->bold ||
                    last->flags != info->flags))
            shape_run++;
        info->shape_run_id = shape_run;
    }
}

Project: libass
File: ass_shaper.c

void ass_shaper_font_data_free(ASS_ShaperFontData *priv)
{
#ifdef CONFIG_HARFBUZZ
    int i;
    for (i = 0; i < ASS_FONT_MAX_FACES; i++)
        if (priv->fonts[i]) {
            free(priv->metrics_data[i]);
            hb_font_destroy(priv->fonts[i]);
            hb_font_funcs_destroy(priv->font_funcs[i]);
        }
    free(priv);
#endif
}

Project: libass
File: ass_shaper.c

void ass_shaper_free(ASS_Shaper *shaper)
{
#ifdef CONFIG_HARFBUZZ
    ass_cache_done(shaper->metrics_cache);
    free(shaper->features);
#endif
    free(shaper->event_text);
    free(shaper->ctypes);
    free(shaper->emblevels);
    free(shaper->cmap);
    free(shaper);
}

Project: libass
File: ass_shaper.c

void ass_shaper_info(ASS_Library *lib)
{
    ass_msg(lib, MSGL_INFO, "Shaper: FriBidi "
            FRIBIDI_VERSION " (SIMPLE)"
#ifdef CONFIG_HARFBUZZ
            " HarfBuzz-ng %s (COMPLEX)", hb_version_string()
#endif
           );
}

Project: libass
File: ass_shaper.c

ASS_Shaper *ass_shaper_new(size_t prealloc)
{
    ASS_Shaper *shaper = calloc(sizeof(*shaper), 1);
    if (!shaper)
        return NULL;

    shaper->base_direction = FRIBIDI_PAR_ON;
    if (!check_allocations(shaper, prealloc))
        goto error;

#ifdef CONFIG_HARFBUZZ
    if (!init_features(shaper))
        goto error;
    shaper->metrics_cache = ass_glyph_metrics_cache_create();
    if (!shaper->metrics_cache)
        goto error;
#endif

    return shaper;

error:
    ass_shaper_free(shaper);
    return NULL;
}

Project: libass

FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info)
{
    int i, ret;

    for (i = 0; i < text_info->length; i++)
        shaper->cmap[i] = i;

    for (i = 0; i < text_info->n_lines; i++) {
        LineInfo *line = text_info->lines + i;
        FriBidiParType dir = FRIBIDI_PAR_ON;

        ret = fribidi_reorder_line(0,
                shaper->ctypes + line->offset, line->len, 0, dir,
                shaper->emblevels + line->offset, NULL,
                shaper->cmap + line->offset);
        if (ret == 0)
            return NULL;
    }

    return shaper->cmap;
}

Project: libass
File: ass_shaper.c

void ass_shaper_set_base_direction(ASS_Shaper *shaper, FriBidiParType dir)
{
    shaper->base_direction = dir;
}

Project: libass
File: ass_shaper.c

void ass_shaper_set_kerning(ASS_Shaper *shaper, int kern)
{
#ifdef CONFIG_HARFBUZZ
    shaper->features[KERN].value = !!kern;
#endif
}

Project: libass
File: ass_shaper.c

void ass_shaper_set_language(ASS_Shaper *shaper, const char *code)
{
#ifdef CONFIG_HARFBUZZ
    hb_language_t lang;

    if (code)
        lang = hb_language_from_string(code, -1);
    else
        lang = HB_LANGUAGE_INVALID;

    shaper->language = lang;
#endif
}

Project: libass
File: ass_shaper.c

void ass_shaper_set_level(ASS_Shaper *shaper, ASS_ShapingLevel level)
{
    shaper->shaping_level = level;
}

Project: libass

int ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info)
{
    int i, ret, last_break;
    FriBidiParType dir;
    GlyphInfo *glyphs = text_info->glyphs;

    if (!check_allocations(shaper, text_info->length))
        return -1;

    last_break = 0;
    for (i = 0; i < text_info->length; i++) {
        shaper->event_text[i] = glyphs[i].symbol;
        if (glyphs[i].symbol == '\n' || i == text_info->length - 1) {
            dir = shaper->base_direction;
            fribidi_get_bidi_types(shaper->event_text + last_break,
                    i - last_break + 1, shaper->ctypes + last_break);
            ret = fribidi_get_par_embedding_levels(shaper->ctypes + last_break,
                    i - last_break + 1, &dir, shaper->emblevels + last_break);
            if (ret == 0)
                return -1;
            last_break = i + 1;
        }
    }

    for (i = 0; i < text_info->length; i++) {
        glyphs[i].shape_run_id += shaper->emblevels[i];
    }

#ifdef CONFIG_HARFBUZZ
    switch (shaper->shaping_level) {
    case ASS_SHAPING_SIMPLE:
        shape_fribidi(shaper, glyphs, text_info->length);
        ass_shaper_skip_characters(text_info);
        break;
    case ASS_SHAPING_COMPLEX:
        shape_harfbuzz(shaper, glyphs, text_info->length);
        break;
    }
#else
        shape_fribidi(shaper, glyphs, text_info->length);
        ass_shaper_skip_characters(text_info);
#endif

    return 0;
}

Project: libass

static void ass_shaper_skip_characters(TextInfo *text_info)
{
    int i;
    GlyphInfo *glyphs = text_info->glyphs;

    for (i = 0; i < text_info->length; i++) {
        if ((glyphs[i].symbol <= 0x202e && glyphs[i].symbol >= 0x202a)
                || (glyphs[i].symbol <= 0x200f && glyphs[i].symbol >= 0x200b)
                || (glyphs[i].symbol <= 0x2063 && glyphs[i].symbol >= 0x2060)
                || glyphs[i].symbol == 0xfeff
                || glyphs[i].symbol == 0x00ad
                || glyphs[i].symbol == 0x034f) {
            glyphs[i].symbol = 0;
            glyphs[i].skip++;
        }
    }
}

Project: libass
File: ass_shaper.c

cached_extents(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
               hb_glyph_extents_t *extents, void *user_data)
{
    FT_Face face = font_data;
    struct ass_shaper_metrics_data *metrics_priv = user_data;
    GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
    if (!metrics)
        return false;

    extents->x_bearing = metrics->metrics.horiBearingX;
    extents->y_bearing = metrics->metrics.horiBearingY;
    extents->width     = metrics->metrics.width;
    extents->height    = -metrics->metrics.height;
    ass_cache_dec_ref(metrics);
    return true;
}

Project: libass
File: ass_shaper.c

cached_h_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
                 void *user_data)
{
    FT_Face face = font_data;
    struct ass_shaper_metrics_data *metrics_priv = user_data;
    GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
    if (!metrics)
        return 0;

    hb_position_t advance = metrics->metrics.horiAdvance;
    ass_cache_dec_ref(metrics);
    return advance;
}

Project: libass
File: ass_shaper.c

cached_h_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
                hb_position_t *x, hb_position_t *y, void *user_data)
{
    return true;
}

Project: libass
File: ass_shaper.c

cached_v_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
                 void *user_data)
{
    FT_Face face = font_data;
    struct ass_shaper_metrics_data *metrics_priv = user_data;
    GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
    if (!metrics)
        return 0;

    hb_position_t advance = metrics->metrics.vertAdvance;
    ass_cache_dec_ref(metrics);
    return advance;
}

Project: libass
File: ass_shaper.c

cached_v_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
                hb_position_t *x, hb_position_t *y, void *user_data)
{
    FT_Face face = font_data;
    struct ass_shaper_metrics_data *metrics_priv = user_data;
    GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
    if (!metrics)
        return false;

    *x = metrics->metrics.horiBearingX - metrics->metrics.vertBearingX;
    *y = metrics->metrics.horiBearingY - (-metrics->metrics.vertBearingY);
    ass_cache_dec_ref(metrics);
    return true;
}

Project: libass

get_cached_metrics(struct ass_shaper_metrics_data *metrics, FT_Face face,
                   hb_codepoint_t unicode, hb_codepoint_t glyph)
{
    GlyphMetricsHashValue *val;
    metrics->hash_key.glyph_index = glyph;
    if (ass_cache_get(metrics->metrics_cache, &metrics->hash_key, &val)) {
        if (val->metrics.width >= 0)
            return val;
        ass_cache_dec_ref(val);
        return NULL;
    }
    if (!val)
        return NULL;

    int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
        | FT_LOAD_IGNORE_TRANSFORM;

    if (FT_Load_Glyph(face, glyph, load_flags)) {
        val->metrics.width = -1;
        ass_cache_commit(val, 1);
        ass_cache_dec_ref(val);
        return NULL;
    }

    memcpy(&val->metrics, &face->glyph->metrics, sizeof(FT_Glyph_Metrics));

    if (metrics->vertical && unicode >= VERTICAL_LOWER_BOUND)
        val->metrics.horiAdvance = val->metrics.vertAdvance;

    ass_cache_commit(val, 1);
    return val;
}

Project: libass
File: ass_shaper.c

get_contour_point(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
                     unsigned int point_index, hb_position_t *x,
                     hb_position_t *y, void *user_data)
{
    FT_Face face = font_data;
    int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
        | FT_LOAD_IGNORE_TRANSFORM;

    if (FT_Load_Glyph(face, glyph, load_flags))
        return false;

    if (point_index >= (unsigned)face->glyph->outline.n_points)
        return false;

    *x = face->glyph->outline.points[point_index].x;
    *y = face->glyph->outline.points[point_index].y;
    return true;
}

Project: libass

get_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode,
          hb_codepoint_t variation, hb_codepoint_t *glyph, void *user_data)
{
    FT_Face face = font_data;
    struct ass_shaper_metrics_data *metrics_priv = user_data;

    if (variation)
        *glyph = FT_Face_GetCharVariantIndex(face, ass_font_index_magic(face, unicode), variation);
    else
        *glyph = FT_Get_Char_Index(face, ass_font_index_magic(face, unicode));
    if (!*glyph)
        return false;

    GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, unicode, *glyph);
    ass_cache_dec_ref(metrics);
    return true;
}

Project: libass

static hb_font_t *get_hb_font(ASS_Shaper *shaper, GlyphInfo *info)
{
    ASS_Font *font = info->font;
    hb_font_t **hb_fonts;

    if (!font->shaper_priv)
        font->shaper_priv = calloc(sizeof(ASS_ShaperFontData), 1);


    hb_fonts = font->shaper_priv->fonts;
    if (!hb_fonts[info->face_index]) {
        hb_fonts[info->face_index] =
            hb_ft_font_create(font->faces[info->face_index], NULL);

        font->shaper_priv->metrics_data[info->face_index] =
            calloc(sizeof(struct ass_shaper_metrics_data), 1);
        struct ass_shaper_metrics_data *metrics =
            font->shaper_priv->metrics_data[info->face_index];
        metrics->metrics_cache = shaper->metrics_cache;
        metrics->vertical = info->font->desc.vertical;

        hb_font_funcs_t *funcs = hb_font_funcs_create();
        font->shaper_priv->font_funcs[info->face_index] = funcs;
        hb_font_funcs_set_glyph_func(funcs, get_glyph,
                metrics, NULL);
        hb_font_funcs_set_glyph_h_advance_func(funcs, cached_h_advance,
                metrics, NULL);
        hb_font_funcs_set_glyph_v_advance_func(funcs, cached_v_advance,
                metrics, NULL);
        hb_font_funcs_set_glyph_h_origin_func(funcs, cached_h_origin,
                metrics, NULL);
        hb_font_funcs_set_glyph_v_origin_func(funcs, cached_v_origin,
                metrics, NULL);
        hb_font_funcs_set_glyph_h_kerning_func(funcs, get_h_kerning,
                metrics, NULL);
        hb_font_funcs_set_glyph_v_kerning_func(funcs, get_v_kerning,
                metrics, NULL);
        hb_font_funcs_set_glyph_extents_func(funcs, cached_extents,
                metrics, NULL);
        hb_font_funcs_set_glyph_contour_point_func(funcs, get_contour_point,
                metrics, NULL);
        hb_font_set_funcs(hb_fonts[info->face_index], funcs,
                font->faces[info->face_index], NULL);
    }

    ass_face_set_size(font->faces[info->face_index], info->font_size);
    update_hb_size(hb_fonts[info->face_index], font->faces[info->face_index]);

    struct ass_shaper_metrics_data *metrics =
        font->shaper_priv->metrics_data[info->face_index];
    metrics->hash_key.font = info->font;
    metrics->hash_key.face_index = info->face_index;
    metrics->hash_key.size = info->font_size;
    metrics->hash_key.scale_x = double_to_d6(info->scale_x);
    metrics->hash_key.scale_y = double_to_d6(info->scale_y);

    return hb_fonts[info->face_index];
}

Project: libass
File: ass_shaper.c

get_v_kerning(hb_font_t *font, void *font_data, hb_codepoint_t first,
                 hb_codepoint_t second, void *user_data)
{
    return 0;
}

Project: libass

hb_shaper_get_run_language(ASS_Shaper *shaper, hb_script_t script)
{
    hb_language_t lang;

    if (shaper->language != HB_LANGUAGE_INVALID)
        return shaper->language;

    lang = script_to_language(script);

    if (lang == HB_LANGUAGE_INVALID)
        lang = hb_language_get_default();

    return lang;
}

Project: libass
File: ass_shaper.c

static bool init_features(ASS_Shaper *shaper)
{
    shaper->features = calloc(sizeof(hb_feature_t), NUM_FEATURES);
    if (!shaper->features)
        return false;

    shaper->n_features = NUM_FEATURES;
    shaper->features[VERT].tag = HB_TAG('v', 'e', 'r', 't');
    shaper->features[VERT].end = UINT_MAX;
    shaper->features[VKNA].tag = HB_TAG('v', 'k', 'n', 'a');
    shaper->features[VKNA].end = UINT_MAX;
    shaper->features[KERN].tag = HB_TAG('k', 'e', 'r', 'n');
    shaper->features[KERN].end = UINT_MAX;
    shaper->features[LIGA].tag = HB_TAG('l', 'i', 'g', 'a');
    shaper->features[LIGA].end = UINT_MAX;
    shaper->features[CLIG].tag = HB_TAG('c', 'l', 'i', 'g');
    shaper->features[CLIG].end = UINT_MAX;

    return true;
}

Project: libass
File: ass_shaper.c

FriBidiParType resolve_base_direction(int enc)
{
    switch (enc) {
        case -1:
            return FRIBIDI_PAR_ON;
        default:
            return FRIBIDI_PAR_LTR;
    }
}

Project: libass

static hb_language_t script_to_language(hb_script_t script)
{
    switch (script) {
        case HB_SCRIPT_ARABIC: return hb_language_from_string("ar", -1); break;
        case HB_SCRIPT_ARMENIAN: return hb_language_from_string("hy", -1); break;
        case HB_SCRIPT_BENGALI: return hb_language_from_string("bn", -1); break;
        case HB_SCRIPT_CANADIAN_ABORIGINAL: return hb_language_from_string("iu", -1); break;
        case HB_SCRIPT_CHEROKEE: return hb_language_from_string("chr", -1); break;
        case HB_SCRIPT_COPTIC: return hb_language_from_string("cop", -1); break;
        case HB_SCRIPT_CYRILLIC: return hb_language_from_string("ru", -1); break;
        case HB_SCRIPT_DEVANAGARI: return hb_language_from_string("hi", -1); break;
        case HB_SCRIPT_GEORGIAN: return hb_language_from_string("ka", -1); break;
        case HB_SCRIPT_GREEK: return hb_language_from_string("el", -1); break;
        case HB_SCRIPT_GUJARATI: return hb_language_from_string("gu", -1); break;
        case HB_SCRIPT_GURMUKHI: return hb_language_from_string("pa", -1); break;
        case HB_SCRIPT_HANGUL: return hb_language_from_string("ko", -1); break;
        case HB_SCRIPT_HEBREW: return hb_language_from_string("he", -1); break;
        case HB_SCRIPT_HIRAGANA: return hb_language_from_string("ja", -1); break;
        case HB_SCRIPT_KANNADA: return hb_language_from_string("kn", -1); break;
        case HB_SCRIPT_KATAKANA: return hb_language_from_string("ja", -1); break;
        case HB_SCRIPT_LAO: return hb_language_from_string("lo", -1); break;
        case HB_SCRIPT_LATIN: return hb_language_from_string("en", -1); break;
        case HB_SCRIPT_MALAYALAM: return hb_language_from_string("ml", -1); break;
        case HB_SCRIPT_MONGOLIAN: return hb_language_from_string("mn", -1); break;
        case HB_SCRIPT_ORIYA: return hb_language_from_string("or", -1); break;
        case HB_SCRIPT_SYRIAC: return hb_language_from_string("syr", -1); break;
        case HB_SCRIPT_TAMIL: return hb_language_from_string("ta", -1); break;
        case HB_SCRIPT_TELUGU: return hb_language_from_string("te", -1); break;
        case HB_SCRIPT_THAI: return hb_language_from_string("th", -1); break;

        case HB_SCRIPT_TIBETAN: return hb_language_from_string("bo", -1); break;

        case HB_SCRIPT_ETHIOPIC: return hb_language_from_string("am", -1); break;
        case HB_SCRIPT_KHMER: return hb_language_from_string("km", -1); break;
        case HB_SCRIPT_MYANMAR: return hb_language_from_string("my", -1); break;
        case HB_SCRIPT_SINHALA: return hb_language_from_string("si", -1); break;
        case HB_SCRIPT_THAANA: return hb_language_from_string("dv", -1); break;

        case HB_SCRIPT_BUHID: return hb_language_from_string("bku", -1); break;
        case HB_SCRIPT_HANUNOO: return hb_language_from_string("hnn", -1); break;
        case HB_SCRIPT_TAGALOG: return hb_language_from_string("tl", -1); break;
        case HB_SCRIPT_TAGBANWA: return hb_language_from_string("tbw", -1); break;

        case HB_SCRIPT_UGARITIC: return hb_language_from_string("uga", -1); break;

        case HB_SCRIPT_BUGINESE: return hb_language_from_string("bug", -1); break;
        case HB_SCRIPT_OLD_PERSIAN: return hb_language_from_string("peo", -1); break;
        case HB_SCRIPT_SYLOTI_NAGRI: return hb_language_from_string("syl", -1); break;

        case HB_SCRIPT_NKO: return hb_language_from_string("nko", -1); break;

        default: return HB_LANGUAGE_INVALID; break;
    }
}

Project: libass

static void set_run_features(ASS_Shaper *shaper, GlyphInfo *info)
{
    if (info->font->desc.vertical)
        shaper->features[VERT].value = shaper->features[VKNA].value = 1;
    else
        shaper->features[VERT].value = shaper->features[VKNA].value = 0;

    if (info->hspacing)
        shaper->features[LIGA].value = shaper->features[CLIG].value = 0;
    else
        shaper->features[LIGA].value = shaper->features[CLIG].value = 1;
}

Project: libass

static void shape_fribidi(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len)
{
    int i;
    FriBidiJoiningType *joins = calloc(sizeof(*joins), len);

    fribidi_get_joining_types(shaper->event_text, len, joins);
    fribidi_join_arabic(shaper->ctypes, len, shaper->emblevels, joins);
    fribidi_shape(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC,
            shaper->emblevels, len, joins, shaper->event_text);

    for (i = 0; i < len; i++) {
        GlyphInfo *info = glyphs + i;
        FT_Face face = info->font->faces[info->face_index];
        info->symbol = shaper->event_text[i];
        info->glyph_index = FT_Get_Char_Index(face, ass_font_index_magic(face, shaper->event_text[i]));
    }

    free(joins);
}

Project: libass

static void shape_harfbuzz(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len)
{
    int i;
    hb_buffer_t *buf = hb_buffer_create();
    hb_segment_properties_t props = HB_SEGMENT_PROPERTIES_DEFAULT;

    for (i = 0; i < len; i++)
        glyphs[i].skip = 1;

    for (i = 0; i < len; i++) {
        int offset = i;
        hb_font_t *font = get_hb_font(shaper, glyphs + offset);
        int level = glyphs[offset].shape_run_id;
        int direction = shaper->emblevels[offset] % 2;

        while (i < (len - 1) && level == glyphs[i+1].shape_run_id)
            i++;

        hb_buffer_pre_allocate(buf, i - offset + 1);
        hb_buffer_add_utf32(buf, shaper->event_text + offset, i - offset + 1,
                0, i - offset + 1);

        props.direction = direction ? HB_DIRECTION_RTL : HB_DIRECTION_LTR;
        props.script = glyphs[offset].script;
        props.language  = hb_shaper_get_run_language(shaper, props.script);
        hb_buffer_set_segment_properties(buf, &props);

        set_run_features(shaper, glyphs + offset);
        hb_shape(font, buf, shaper->features, shaper->n_features);

        shape_harfbuzz_process_run(glyphs, buf, offset);
        hb_buffer_reset(buf);
    }

    hb_buffer_destroy(buf);
}

Project: libass

shape_harfbuzz_process_run(GlyphInfo *glyphs, hb_buffer_t *buf, int offset)
{
    int j;
    int num_glyphs = hb_buffer_get_length(buf);
    hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, NULL);
    hb_glyph_position_t *pos    = hb_buffer_get_glyph_positions(buf, NULL);

    for (j = 0; j < num_glyphs; j++) {
        unsigned idx = glyph_info[j].cluster + offset;
        GlyphInfo *info = glyphs + idx;
        GlyphInfo *root = info;

        if (info->skip == 0) {
            while (info->next)
                info = info->next;
            info->next = malloc(sizeof(GlyphInfo));
            if (info->next) {
                memcpy(info->next, info, sizeof(GlyphInfo));
                ass_cache_inc_ref(info->font);
                info = info->next;
                info->next = NULL;
            }
        }

        info->skip = 0;
        info->glyph_index = glyph_info[j].codepoint;
        info->offset.x    = pos[j].x_offset * info->scale_x;
        info->offset.y    = -pos[j].y_offset * info->scale_y;
        info->advance.x   = pos[j].x_advance * info->scale_x;
        info->advance.y   = -pos[j].y_advance * info->scale_y;

        root->cluster_advance.x += info->advance.x;
        root->cluster_advance.y += info->advance.y;
    }
}

Project: libass
File: ass_shaper.c

static void update_hb_size(hb_font_t *hb_font, FT_Face face)
{
    hb_font_set_scale (hb_font,
            ((uint64_t) face->size->metrics.x_scale * (uint64_t) face->units_per_EM) >> 16,
            ((uint64_t) face->size->metrics.y_scale * (uint64_t) face->units_per_EM) >> 16);
    hb_font_set_ppem (hb_font, face->size->metrics.x_ppem,
            face->size->metrics.y_ppem);
}