]> granicus.if.org Git - libass/blob - libass/ass_shaper.c
Remove a bunch of debug printf calls
[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 <fribidi/fribidi.h>
20 #include <hb-ft.h>
21
22 #include "ass_shaper.h"
23 #include "ass_render.h"
24 #include "ass_font.h"
25 #include "ass_parse.h"
26 #include "ass_cache.h"
27
28 #define MAX_RUNS 50
29
30 enum {
31     VERT = 0,
32     VKNA,
33     KERN
34 };
35 #define NUM_FEATURES 3
36
37 struct ass_shaper {
38     // FriBidi log2vis
39     int n_glyphs;
40     FriBidiChar *event_text;
41     FriBidiCharType *ctypes;
42     FriBidiLevel *emblevels;
43     FriBidiStrIndex *cmap;
44     FriBidiParType base_direction;
45     // OpenType features
46     int n_features;
47     hb_feature_t *features;
48     hb_language_t language;
49     // Glyph metrics cache, to speed up shaping
50     Cache *metrics_cache;
51 };
52
53 struct ass_shaper_metrics_data {
54     Cache *metrics_cache;
55     GlyphMetricsHashKey hash_key;
56 };
57
58 struct ass_shaper_font_data {
59     hb_font_t *fonts[ASS_FONT_MAX_FACES];
60     hb_font_funcs_t *font_funcs[ASS_FONT_MAX_FACES];
61     struct ass_shaper_metrics_data *metrics_data[ASS_FONT_MAX_FACES];
62 };
63
64 /**
65  * \brief Print version information
66  */
67 void ass_shaper_info(ASS_Library *lib)
68 {
69     ass_msg(lib, MSGL_V, "Complex text layout enabled, using FriBidi "
70             FRIBIDI_VERSION " HarfBuzz-ng %s", hb_version_string());
71 }
72
73 /**
74  * \brief grow arrays, if needed
75  * \param new_size requested size
76  */
77 static void check_allocations(ASS_Shaper *shaper, size_t new_size)
78 {
79     if (new_size > shaper->n_glyphs) {
80         shaper->event_text = realloc(shaper->event_text, sizeof(FriBidiChar) * new_size);
81         shaper->ctypes     = realloc(shaper->ctypes, sizeof(FriBidiCharType) * new_size);
82         shaper->emblevels  = realloc(shaper->emblevels, sizeof(FriBidiLevel) * new_size);
83         shaper->cmap       = realloc(shaper->cmap, sizeof(FriBidiStrIndex) * new_size);
84     }
85 }
86
87 /**
88  * \brief set up the HarfBuzz OpenType feature list with some
89  * standard features.
90  */
91 static void init_features(ASS_Shaper *shaper)
92 {
93     shaper->features = calloc(sizeof(hb_feature_t), NUM_FEATURES);
94
95     shaper->n_features = NUM_FEATURES;
96     shaper->features[VERT].tag = HB_TAG('v', 'e', 'r', 't');
97     shaper->features[VERT].end = INT_MAX;
98     shaper->features[VKNA].tag = HB_TAG('v', 'k', 'n', 'a');
99     shaper->features[VKNA].end = INT_MAX;
100     shaper->features[KERN].tag = HB_TAG('k', 'e', 'r', 'n');
101     shaper->features[KERN].end = INT_MAX;
102 }
103
104 /**
105  * \brief Create a new shaper instance and preallocate data structures
106  * \param prealloc preallocation size
107  */
108 ASS_Shaper *ass_shaper_new(size_t prealloc)
109 {
110     ASS_Shaper *shaper = calloc(sizeof(*shaper), 1);
111
112     shaper->base_direction = FRIBIDI_PAR_ON;
113     init_features(shaper);
114     check_allocations(shaper, prealloc);
115
116     shaper->metrics_cache = ass_glyph_metrics_cache_create();
117
118     return shaper;
119 }
120
121 /**
122  * \brief Free shaper and related data
123  */
124 void ass_shaper_free(ASS_Shaper *shaper)
125 {
126     ass_cache_done(shaper->metrics_cache);
127     free(shaper->event_text);
128     free(shaper->ctypes);
129     free(shaper->emblevels);
130     free(shaper->cmap);
131     free(shaper->features);
132     free(shaper);
133 }
134
135 void ass_shaper_font_data_free(ASS_ShaperFontData *priv)
136 {
137     int i;
138     for (i = 0; i < ASS_FONT_MAX_FACES; i++)
139         if (priv->fonts[i]) {
140             free(priv->metrics_data[i]);
141             hb_font_destroy(priv->fonts[i]);
142             hb_font_funcs_destroy(priv->font_funcs[i]);
143         }
144     free(priv);
145 }
146
147 /**
148  * \brief Set features depending on properties of the run
149  */
150 static void set_run_features(ASS_Shaper *shaper, GlyphInfo *info)
151 {
152         // enable vertical substitutions for @font runs
153         if (info->font->desc.vertical)
154             shaper->features[VERT].value = shaper->features[VKNA].value = 1;
155         else
156             shaper->features[VERT].value = shaper->features[VKNA].value = 0;
157 }
158
159 /**
160  * \brief Update HarfBuzz's idea of font metrics
161  * \param hb_font HarfBuzz font
162  * \param face associated FreeType font face
163  */
164 static void update_hb_size(hb_font_t *hb_font, FT_Face face)
165 {
166     hb_font_set_scale (hb_font,
167             ((uint64_t) face->size->metrics.x_scale * (uint64_t) face->units_per_EM) >> 16,
168             ((uint64_t) face->size->metrics.y_scale * (uint64_t) face->units_per_EM) >> 16);
169     hb_font_set_ppem (hb_font, face->size->metrics.x_ppem,
170             face->size->metrics.y_ppem);
171 }
172
173
174 /*
175  * Cached glyph metrics getters follow
176  *
177  * These functions replace HarfBuzz' standard FreeType font functions
178  * and provide cached access to essential glyph metrics. This usually
179  * speeds up shaping a lot. It also allows us to use custom load flags.
180  *
181  */
182
183 GlyphMetricsHashValue *
184 get_cached_metrics(struct ass_shaper_metrics_data *metrics, FT_Face face,
185                    hb_codepoint_t glyph)
186 {
187     GlyphMetricsHashValue *val;
188
189     metrics->hash_key.glyph_index = glyph;
190     val = ass_cache_get(metrics->metrics_cache, &metrics->hash_key);
191
192     if (!val) {
193         int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
194             | FT_LOAD_IGNORE_TRANSFORM;
195         GlyphMetricsHashValue new_val;
196
197         if (FT_Load_Glyph(face, glyph, load_flags))
198             return NULL;
199
200         memcpy(&new_val.metrics, &face->glyph->metrics, sizeof(FT_Glyph_Metrics));
201         val = ass_cache_put(metrics->metrics_cache, &metrics->hash_key, &new_val);
202     }
203
204     return val;
205 }
206
207 static hb_bool_t
208 get_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode,
209           hb_codepoint_t variation, hb_codepoint_t *glyph, void *user_data)
210 {
211     FT_Face face = font_data;
212
213     if (variation)
214         *glyph = FT_Face_GetCharVariantIndex(face, unicode, variation);
215     else
216         *glyph = FT_Get_Char_Index(face, unicode);
217
218     return *glyph != 0;
219 }
220
221 static hb_position_t
222 cached_h_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
223                  void *user_data)
224 {
225     FT_Face face = font_data;
226     struct ass_shaper_metrics_data *metrics_priv = user_data;
227     GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph);
228
229     if (!metrics)
230         return 0;
231
232     return metrics->metrics.horiAdvance;
233 }
234
235 static hb_position_t
236 cached_v_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
237                  void *user_data)
238 {
239     FT_Face face = font_data;
240     struct ass_shaper_metrics_data *metrics_priv = user_data;
241     GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph);
242
243     if (!metrics)
244         return 0;
245
246     return metrics->metrics.vertAdvance;
247
248 }
249
250 static hb_bool_t
251 cached_h_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
252                 hb_position_t *x, hb_position_t *y, void *user_data)
253 {
254     return 1;
255 }
256
257 static hb_bool_t
258 cached_v_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
259                 hb_position_t *x, hb_position_t *y, void *user_data)
260 {
261     FT_Face face = font_data;
262     struct ass_shaper_metrics_data *metrics_priv = user_data;
263     GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph);
264
265     if (!metrics)
266         return 0;
267
268     *x = metrics->metrics.horiBearingX - metrics->metrics.vertBearingX;
269     *y = metrics->metrics.horiBearingY - (-metrics->metrics.vertBearingY);
270
271     return 1;
272 }
273
274 static hb_position_t
275 get_h_kerning(hb_font_t *font, void *font_data, hb_codepoint_t first,
276                  hb_codepoint_t second, void *user_data)
277 {
278     FT_Face face = font_data;
279     FT_Vector kern;
280
281     if (FT_Get_Kerning (face, first, second, FT_KERNING_DEFAULT, &kern))
282         return 0;
283
284     return kern.x;
285 }
286
287 static hb_position_t
288 get_v_kerning(hb_font_t *font, void *font_data, hb_codepoint_t first,
289                  hb_codepoint_t second, void *user_data)
290 {
291     return 0;
292 }
293
294 static hb_bool_t
295 cached_extents(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
296                hb_glyph_extents_t *extents, void *user_data)
297 {
298     FT_Face face = font_data;
299     struct ass_shaper_metrics_data *metrics_priv = user_data;
300     GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph);
301
302     if (!metrics)
303         return 0;
304
305     extents->x_bearing = metrics->metrics.horiBearingX;
306     extents->y_bearing = metrics->metrics.horiBearingY;
307     extents->width     = metrics->metrics.width;
308     extents->height    = metrics->metrics.height;
309
310     return 1;
311 }
312
313 static hb_bool_t
314 get_contour_point(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
315                      unsigned int point_index, hb_position_t *x,
316                      hb_position_t *y, void *user_data)
317 {
318     FT_Face face = font_data;
319     int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
320         | FT_LOAD_IGNORE_TRANSFORM;
321
322     if (FT_Load_Glyph(face, glyph, load_flags))
323         return 0;
324
325     if (point_index >= (unsigned)face->glyph->outline.n_points)
326         return 0;
327
328     *x = face->glyph->outline.points[point_index].x;
329     *y = face->glyph->outline.points[point_index].y;
330
331     return 1;
332 }
333
334 /**
335  * \brief Retrieve HarfBuzz font from cache.
336  * Create it from FreeType font, if needed.
337  * \param info glyph cluster
338  * \return HarfBuzz font
339  */
340 static hb_font_t *get_hb_font(ASS_Shaper *shaper, GlyphInfo *info)
341 {
342     ASS_Font *font = info->font;
343     hb_font_t **hb_fonts;
344
345     if (!font->shaper_priv)
346         font->shaper_priv = calloc(sizeof(ASS_ShaperFontData), 1);
347
348
349     hb_fonts = font->shaper_priv->fonts;
350     if (!hb_fonts[info->face_index]) {
351         hb_fonts[info->face_index] =
352             hb_ft_font_create(font->faces[info->face_index], NULL);
353
354         // set up cached metrics access
355         font->shaper_priv->metrics_data[info->face_index] =
356             calloc(sizeof(struct ass_shaper_metrics_data), 1);
357         struct ass_shaper_metrics_data *metrics =
358             font->shaper_priv->metrics_data[info->face_index];
359         metrics->metrics_cache = shaper->metrics_cache;
360
361         hb_font_funcs_t *funcs = hb_font_funcs_create();
362         font->shaper_priv->font_funcs[info->face_index] = funcs;
363         hb_font_funcs_set_glyph_func(funcs, get_glyph,
364                 metrics, NULL);
365         hb_font_funcs_set_glyph_h_advance_func(funcs, cached_h_advance,
366                 metrics, NULL);
367         hb_font_funcs_set_glyph_v_advance_func(funcs, cached_v_advance,
368                 metrics, NULL);
369         hb_font_funcs_set_glyph_h_origin_func(funcs, cached_h_origin,
370                 metrics, NULL);
371         hb_font_funcs_set_glyph_v_origin_func(funcs, cached_v_origin,
372                 metrics, NULL);
373         hb_font_funcs_set_glyph_h_kerning_func(funcs, get_h_kerning,
374                 metrics, NULL);
375         hb_font_funcs_set_glyph_v_kerning_func(funcs, get_v_kerning,
376                 metrics, NULL);
377         hb_font_funcs_set_glyph_extents_func(funcs, cached_extents,
378                 metrics, NULL);
379         hb_font_funcs_set_glyph_contour_point_func(funcs, get_contour_point,
380                 metrics, NULL);
381         hb_font_set_funcs(hb_fonts[info->face_index], funcs,
382                 font->faces[info->face_index], NULL);
383     }
384
385     ass_face_set_size(font->faces[info->face_index], info->font_size);
386     update_hb_size(hb_fonts[info->face_index], font->faces[info->face_index]);
387
388     // update hash key for cached metrics
389     struct ass_shaper_metrics_data *metrics =
390         font->shaper_priv->metrics_data[info->face_index];
391     metrics->hash_key.font = info->font;
392     metrics->hash_key.face_index = info->face_index;
393     metrics->hash_key.size = info->font_size;
394     metrics->hash_key.scale_x = double_to_d6(info->scale_x);
395     metrics->hash_key.scale_y = double_to_d6(info->scale_y);
396
397     return hb_fonts[info->face_index];
398 }
399
400 /**
401  * \brief Shape event text with HarfBuzz. Full OpenType shaping.
402  * \param glyphs glyph clusters
403  * \param len number of clusters
404  */
405 static void shape_harfbuzz(ASS_Shaper *shaper, GlyphInfo *glyphs, size_t len)
406 {
407     int i, j;
408     int run = 0;
409     struct {
410         int offset;
411         int end;
412         hb_buffer_t *buf;
413         hb_font_t *font;
414     } runs[MAX_RUNS];
415
416
417     for (i = 0; i < len && run < MAX_RUNS; i++, run++) {
418         // get length and level of the current run
419         int k = i;
420         int level = glyphs[i].shape_run_id;
421         int direction = shaper->emblevels[k] % 2;
422         while (i < (len - 1) && level == glyphs[i+1].shape_run_id)
423             i++;
424         runs[run].offset = k;
425         runs[run].end    = i;
426         runs[run].buf    = hb_buffer_create(i - k + 1);
427         runs[run].font   = get_hb_font(shaper, glyphs + k);
428         set_run_features(shaper, glyphs + k);
429         hb_buffer_set_direction(runs[run].buf, direction ? HB_DIRECTION_RTL :
430                 HB_DIRECTION_LTR);
431         hb_buffer_set_language(runs[run].buf, shaper->language);
432         hb_buffer_add_utf32(runs[run].buf, shaper->event_text + k, i - k + 1,
433                 0, i - k + 1);
434         hb_shape(runs[run].font, runs[run].buf, shaper->features,
435                 shaper->n_features);
436     }
437
438     // Initialize: skip all glyphs, this is undone later as needed
439     for (i = 0; i < len; i++)
440         glyphs[i].skip = 1;
441
442     // Update glyph indexes, positions and advances from the shaped runs
443     for (i = 0; i < run; i++) {
444         int num_glyphs = hb_buffer_get_length(runs[i].buf);
445         hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(runs[i].buf, NULL);
446         hb_glyph_position_t *pos    = hb_buffer_get_glyph_positions(runs[i].buf, NULL);
447
448         for (j = 0; j < num_glyphs; j++) {
449             int idx = glyph_info[j].cluster + runs[i].offset;
450             GlyphInfo *info = glyphs + idx;
451             GlyphInfo *root = info;
452
453             // if we have more than one glyph per cluster, allocate a new one
454             // and attach to the root glyph
455             if (info->skip == 0) {
456                 while (info->next)
457                     info = info->next;
458                 info->next = malloc(sizeof(GlyphInfo));
459                 memcpy(info->next, info, sizeof(GlyphInfo));
460                 info = info->next;
461                 info->next = NULL;
462             }
463
464             // set position and advance
465             info->skip = 0;
466             info->glyph_index = glyph_info[j].codepoint;
467             info->offset.x    = pos[j].x_offset * info->scale_x;
468             info->offset.y    = -pos[j].y_offset * info->scale_y;
469             info->advance.x   = pos[j].x_advance * info->scale_x;
470             info->advance.y   = -pos[j].y_advance * info->scale_y;
471
472             // accumulate advance in the root glyph
473             root->cluster_advance.x += info->advance.x;
474             root->cluster_advance.y += info->advance.y;
475         }
476     }
477
478     // Free runs and associated data
479     for (i = 0; i < run; i++) {
480         hb_buffer_destroy(runs[i].buf);
481     }
482
483 }
484
485 /**
486  * \brief Shape event text with FriBidi. Does mirroring and simple
487  * Arabic shaping.
488  * \param len number of clusters
489  */
490 static void shape_fribidi(ASS_Shaper *shaper, size_t len)
491 {
492     FriBidiJoiningType *joins = calloc(sizeof(*joins), len);
493
494     fribidi_get_joining_types(shaper->event_text, len, joins);
495     fribidi_join_arabic(shaper->ctypes, len, shaper->emblevels, joins);
496     fribidi_shape(FRIBIDI_FLAGS_DEFAULT | FRIBIDI_FLAGS_ARABIC,
497             shaper->emblevels, len, joins, shaper->event_text);
498
499     free(joins);
500 }
501
502 /**
503  * \brief Toggle kerning for HarfBuzz shaping.
504  * NOTE: currently only works with OpenType fonts, the TrueType fallback *always*
505  * kerns. It's a bug in HarfBuzz.
506  */
507 void ass_shaper_set_kerning(ASS_Shaper *shaper, int kern)
508 {
509     shaper->features[KERN].value = !!kern;
510 }
511
512 /**
513  * \brief Find shape runs according to the event's selected fonts
514  */
515 void ass_shaper_find_runs(ASS_Shaper *shaper, ASS_Renderer *render_priv,
516                           GlyphInfo *glyphs, size_t len)
517 {
518     int i;
519     int shape_run = 0;
520
521     for (i = 0; i < len; i++) {
522         GlyphInfo *last = glyphs + i - 1;
523         GlyphInfo *info = glyphs + i;
524         // skip drawings
525         if (info->symbol == 0xfffc)
526             continue;
527         // set size and get glyph index
528         ass_font_get_index(render_priv->fontconfig_priv, info->font,
529                 info->symbol, &info->face_index, &info->glyph_index);
530         // shape runs share the same font face and size
531         if (i > 0 && (last->font != info->font ||
532                     last->font_size != info->font_size ||
533                     last->face_index != info->face_index))
534             shape_run++;
535         info->shape_run_id = shape_run;
536     }
537
538 }
539
540 /**
541  * \brief Set base direction (paragraph direction) of the text.
542  * \param dir base direction
543  */
544 void ass_shaper_set_base_direction(ASS_Shaper *shaper, FriBidiParType dir)
545 {
546     shaper->base_direction = dir;
547 }
548
549 /**
550  * \brief Set language hint. Some languages have specific character variants,
551  * like Serbian Cyrillic.
552  * \param lang ISO 639-1 two-letter language code
553  */
554 void ass_shaper_set_language(ASS_Shaper *shaper, const char *code)
555 {
556     shaper->language = hb_language_from_string(code);
557 }
558
559 /**
560  * \brief Shape an event's text. Calculates directional runs and shapes them.
561  * \param text_info event's text
562  */
563 void ass_shaper_shape(ASS_Shaper *shaper, TextInfo *text_info)
564 {
565     int i, last_break;
566     FriBidiParType dir;
567     GlyphInfo *glyphs = text_info->glyphs;
568
569     check_allocations(shaper, text_info->length);
570
571     // Get bidi character types and embedding levels
572     last_break = 0;
573     for (i = 0; i < text_info->length; i++) {
574         shaper->event_text[i] = glyphs[i].symbol;
575         // embedding levels should be calculated paragraph by paragraph
576         if (glyphs[i].symbol == '\n' || i == text_info->length - 1) {
577             dir = shaper->base_direction;
578             fribidi_get_bidi_types(shaper->event_text + last_break,
579                     i - last_break + 1, shaper->ctypes + last_break);
580             fribidi_get_par_embedding_levels(shaper->ctypes + last_break,
581                     i - last_break + 1, &dir, shaper->emblevels + last_break);
582             last_break = i + 1;
583         }
584     }
585
586     // add embedding levels to shape runs for final runs
587     for (i = 0; i < text_info->length; i++) {
588         glyphs[i].shape_run_id += shaper->emblevels[i];
589     }
590
591     //shape_fribidi(shaper, text_info->length);
592     shape_harfbuzz(shaper, glyphs, text_info->length);
593
594     // Update glyphs
595     for (i = 0; i < text_info->length; i++) {
596         glyphs[i].symbol = shaper->event_text[i];
597         // Skip direction override control characters
598         // NOTE: Behdad said HarfBuzz is supposed to remove these, but this hasn't
599         // been implemented yet
600         if (glyphs[i].symbol <= 0x202F && glyphs[i].symbol >= 0x202a) {
601             glyphs[i].symbol = 0;
602             glyphs[i].skip++;
603         }
604     }
605 }
606
607 /**
608  * \brief clean up additional data temporarily needed for shaping and
609  * (e.g. additional glyphs allocated)
610  */
611 void ass_shaper_cleanup(ASS_Shaper *shaper, TextInfo *text_info)
612 {
613     int i;
614
615     for (i = 0; i < text_info->length; i++) {
616         GlyphInfo *info = text_info->glyphs + i;
617         info = info->next;
618         while (info) {
619             GlyphInfo *next = info->next;
620             free(info);
621             info = next;
622         }
623     }
624 }
625
626 /**
627  * \brief Calculate reorder map to render glyphs in visual order
628  */
629 FriBidiStrIndex *ass_shaper_reorder(ASS_Shaper *shaper, TextInfo *text_info)
630 {
631     int i;
632
633     // Initialize reorder map
634     for (i = 0; i < text_info->length; i++)
635         shaper->cmap[i] = i;
636
637     // Create reorder map line-by-line
638     for (i = 0; i < text_info->n_lines; i++) {
639         LineInfo *line = text_info->lines + i;
640         int level;
641         FriBidiParType dir = FRIBIDI_PAR_ON;
642
643         level = fribidi_reorder_line(0,
644                 shaper->ctypes + line->offset, line->len, 0, dir,
645                 shaper->emblevels + line->offset, NULL,
646                 shaper->cmap + line->offset);
647     }
648
649     return shaper->cmap;
650 }
651
652 /**
653  * \brief Resolve a Windows font encoding number to a suitable
654  * base direction. 177 and 178 are Hebrew and Arabic respectively, and
655  * they map to RTL. 1 is autodetection and is mapped to just that.
656  * Everything else is mapped to LTR.
657  * \param enc Windows font encoding
658  */
659 FriBidiParType resolve_base_direction(int enc)
660 {
661     switch (enc) {
662         case 1:
663             return FRIBIDI_PAR_ON;
664         case 177:
665         case 178:
666             return FRIBIDI_PAR_RTL;
667         default:
668             return FRIBIDI_PAR_LTR;
669     }
670 }