2 * Copyright (C) 2011 Grigori Goronzy <greg@chown.ath.cx>
4 * This file is part of libass.
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.
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.
19 #include <fribidi/fribidi.h>
22 #include "ass_render.h"
23 #include "ass_shaper.h"
28 * \brief Print version information
30 void ass_shaper_info(ASS_Library *lib)
32 ass_msg(lib, MSGL_V, "Complex text layout enabled, using FriBidi "
33 FRIBIDI_VERSION " HarfBuzz-ng %s", hb_version_string());
37 * \brief Shape an event's text. Calculates directional runs and shapes them.
38 * \param text_info event's text
39 * \param ctypes returns character types
40 * \param emblevels returns embedding levels (directional runs)
42 void ass_shaper_shape(TextInfo *text_info, FriBidiCharType *ctypes,
43 FriBidiLevel *emblevels)
47 FriBidiChar *event_text = calloc(sizeof(*event_text), text_info->length);
48 FriBidiJoiningType *joins = calloc(sizeof(*joins), text_info->length);
49 GlyphInfo *glyphs = text_info->glyphs;
50 // XXX: dynamically allocate
58 // Get bidi character types and embedding levels
60 for (i = 0; i < text_info->length; i++) {
61 event_text[i] = glyphs[i].symbol;
62 // embedding levels should be calculated paragraph by paragraph
63 if (glyphs[i].symbol == '\n' || i == text_info->length - 1) {
64 //printf("paragraph from %d to %d\n", last_break, i);
66 fribidi_get_bidi_types(event_text + last_break, i - last_break + 1,
68 fribidi_get_par_embedding_levels(ctypes + last_break,
69 i - last_break + 1, &dir, emblevels + last_break);
74 // add embedding levels to shape runs for final runs
75 for (i = 0; i < text_info->length; i++) {
76 glyphs[i].shape_run_id += emblevels[i];
81 for (i = 0; i < text_info->length; i++) {
82 printf("%d ", glyphs[i].shape_run_id);
88 // Use FriBidi's shaper for mirroring and simple Arabic shaping
89 fribidi_get_joining_types(event_text, text_info->length, joins);
90 fribidi_join_arabic(ctypes, text_info->length, emblevels, joins);
91 fribidi_shape(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC, emblevels,
92 text_info->length, joins, event_text);
95 // Shape runs with HarfBuzz-ng
97 for (i = 0; i < text_info->length && run < MAX_RUNS; i++, run++) {
98 // get length and level of the current run
100 int level = glyphs[i].shape_run_id;
101 int direction = emblevels[k] % 2;
102 while (i < (text_info->length - 1) && level == glyphs[i+1].shape_run_id)
104 //printf("run %d from %d to %d with level %d\n", run, k, i, level);
105 FT_Face run_font = glyphs[k].font->faces[glyphs[k].face_index];
106 runs[run].offset = k;
108 runs[run].buf = hb_buffer_create(i - k + 1);
109 runs[run].font = hb_ft_font_create(run_font, NULL);
110 hb_buffer_set_direction(runs[run].buf, direction ? HB_DIRECTION_RTL :
112 hb_buffer_add_utf32(runs[run].buf, event_text + k, i - k + 1,
114 hb_shape(runs[run].font, runs[run].buf, NULL, 0);
116 //printf("shaped %d runs\n", run);
118 // Initialize: skip all glyphs, this is undone later as needed
119 for (i = 0; i < text_info->length; i++)
122 // Update glyph indexes, positions and advances from the shaped runs
123 for (i = 0; i < run; i++) {
124 int num_glyphs = hb_buffer_get_length(runs[i].buf);
125 hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(runs[i].buf, NULL);
126 hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(runs[i].buf, NULL);
127 //printf("run text len %d num_glyphs %d\n", runs[i].end - runs[i].offset + 1,
130 for (j = 0; j < num_glyphs; j++) {
131 int idx = glyph_info[j].cluster + runs[i].offset;
132 GlyphInfo *info = glyphs + idx;
133 GlyphInfo *root = info;
135 printf("run %d cluster %d codepoint %d -> '%c'\n", i, idx,
136 glyph_info[j].codepoint, event_text[idx]);
137 printf("position %d %d advance %d %d\n",
138 pos[j].x_offset, pos[j].y_offset,
139 pos[j].x_advance, pos[j].y_advance);
142 // if we have more than one glyph per cluster, allocate a new one
143 // and attach to the root glyph
144 if (info->skip == 0) {
145 //printf("duplicate cluster entry, adding glyph\n");
148 info->next = malloc(sizeof(GlyphInfo));
149 memcpy(info->next, info, sizeof(GlyphInfo));
154 // set position and advance
156 info->glyph_index = glyph_info[j].codepoint;
157 info->offset.x = pos[j].x_offset * info->scale_x;
158 info->offset.y = -pos[j].y_offset * info->scale_y;
159 info->advance.x = pos[j].x_advance * info->scale_x;
160 info->advance.y = -pos[j].y_advance * info->scale_y;
162 // accumulate advance in the root glyph
163 root->cluster_advance.x += info->advance.x;
164 root->cluster_advance.y += info->advance.y;
168 // Free runs and associated data
169 for (i = 0; i < run; i++) {
170 hb_buffer_destroy(runs[i].buf);
171 hb_font_destroy(runs[i].font);
175 for (i = 0; i < text_info->length; i++) {
176 glyphs[i].symbol = event_text[i];
177 // Skip direction override control characters
178 // NOTE: Behdad said HarfBuzz is supposed to remove these, but this hasn't
179 // been implemented yet
180 if (glyphs[i].symbol <= 0x202F && glyphs[i].symbol >= 0x202a) {
181 glyphs[i].symbol = 0;
191 * \brief clean up additional data temporarily needed for shaping and
192 * (e.g. additional glyphs allocated)
194 void ass_shaper_cleanup(TextInfo *text_info)
198 for (i = 0; i < text_info->length; i++) {
199 GlyphInfo *info = text_info->glyphs + i;
202 GlyphInfo *next = info->next;
209 void ass_shaper_reorder(TextInfo *text_info, FriBidiCharType *ctypes,
210 FriBidiLevel *emblevels, FriBidiStrIndex *cmap)
215 // Initialize reorder map
216 for (i = 0; i < text_info->length; i++)
219 // Create reorder map line-by-line
220 for (i = 0; i < text_info->n_lines; i++) {
221 LineInfo *line = text_info->lines + i;
223 dir = FRIBIDI_PAR_ON;
225 // FIXME: we should actually specify
226 // the correct paragraph base direction
227 level = fribidi_reorder_line(FRIBIDI_FLAGS_DEFAULT,
228 ctypes + line->offset, line->len, 0, dir,
229 emblevels + line->offset, NULL, cmap + line->offset);
230 //printf("reorder line %d to level %d\n", i, level);
235 for (i = 0; i < text_info->length; i++) {
236 printf("%d ", cmap[i]);