Fix order of header inclusion.
[ttfautohint.git] / lib / taharfbuzz.c
blob3f6f78755fa31f225ab0e61348de3fc91706e59c
1 /* taharfbuzz.c */
3 /*
4 * Copyright (C) 2014 by Werner Lemberg.
6 * This file is part of the ttfautohint library, and may only be used,
7 * modified, and distributed under the terms given in `COPYING'. By
8 * continuing to use, modify, or distribute this file you indicate that you
9 * have read `COPYING' and understand and accept it fully.
11 * The file `COPYING' mentioned in the previous paragraph is distributed
12 * with the ttfautohint library.
16 /* originally file `hbshim.c' (2014-Jan-11) from FreeType */
18 /* heavily modified 2014 by Werner Lemberg <wl@gnu.org> */
20 #include "taharfbuzz.h"
24 * We use `sets' (in the HarfBuzz sense, which comes quite near to the
25 * usual mathematical meaning) to manage both lookups and glyph indices.
27 * 1. For each coverage, collect lookup IDs in a set. Note that an
28 * auto-hinter `coverage' is represented by one `feature', and a
29 * feature consists of an arbitrary number of (font specific) `lookup's
30 * that actually do the mapping job. Please check the OpenType
31 * specification for more details on features and lookups.
33 * 2. Create glyph ID sets from the corresponding lookup sets.
35 * 3. The glyph set corresponding to TA_COVERAGE_DEFAULT is computed
36 * with all lookups specific to the OpenType script activated. It
37 * relies on the order of ta_style_classes entries so that
38 * special coverages (like `oldstyle figures') don't get overwritten.
42 /* load coverage tags */
43 #undef COVERAGE
44 #define COVERAGE(name, NAME, description, \
45 tag1, tag2, tag3, tag4) \
46 static const hb_tag_t name ## _coverage[] = \
47 { \
48 HB_TAG(tag1, tag2, tag3, tag4), \
49 HB_TAG_NONE \
52 #include "tacover.h"
55 /* define mapping between coverage tags and TA_Coverage */
56 #undef COVERAGE
57 #define COVERAGE(name, NAME, description, \
58 tag1, tag2, tag3, tag4) \
59 name ## _coverage,
61 static const hb_tag_t* coverages[] =
63 #include "tacover.h"
65 NULL /* TA_COVERAGE_DEFAULT */
69 /* load HarfBuzz script tags */
70 #undef SCRIPT
71 #define SCRIPT(s, S, d, h, sc1, sc2, sc3) h,
73 static const hb_script_t scripts[] =
75 #include "ttfautohint-scripts.h"
79 FT_Error
80 ta_get_coverage(TA_FaceGlobals globals,
81 TA_StyleClass style_class,
82 FT_Byte* gstyles)
84 hb_face_t* face;
86 hb_set_t* gsub_lookups; /* GSUB lookups for a given script */
87 hb_set_t* gsub_glyphs; /* glyphs covered by GSUB lookups */
88 hb_set_t* gpos_lookups; /* GPOS lookups for a given script */
89 hb_set_t* gpos_glyphs; /* glyphs covered by GPOS lookups */
91 hb_script_t script;
92 const hb_tag_t* coverage_tags;
93 hb_tag_t script_tags[] = { HB_TAG_NONE,
94 HB_TAG_NONE,
95 HB_TAG_NONE,
96 HB_TAG_NONE };
98 hb_codepoint_t idx;
99 #ifdef TA_DEBUG
100 int count;
101 #endif
104 if (!globals || !style_class || !gstyles)
105 return FT_Err_Invalid_Argument;
107 face = hb_font_get_face(globals->hb_font);
109 gsub_lookups = hb_set_create();
110 gsub_glyphs = hb_set_create();
111 gpos_lookups = hb_set_create();
112 gpos_glyphs = hb_set_create();
114 coverage_tags = coverages[style_class->coverage];
115 script = scripts[style_class->script];
117 /* convert a HarfBuzz script tag into the corresponding OpenType */
118 /* tag or tags -- some Indic scripts like Devanagari have an old */
119 /* and a new set of features */
120 hb_ot_tags_from_script(script,
121 &script_tags[0],
122 &script_tags[1]);
124 /* `hb_ot_tags_from_script' usually returns HB_OT_TAG_DEFAULT_SCRIPT */
125 /* as the second tag; we change that to HB_TAG_NONE except for the */
126 /* default script */
127 if (style_class->script == globals->font->default_script
128 && style_class->coverage == TA_COVERAGE_DEFAULT)
130 if (script_tags[0] == HB_TAG_NONE)
131 script_tags[0] = HB_OT_TAG_DEFAULT_SCRIPT;
132 else
134 if (script_tags[1] == HB_TAG_NONE)
135 script_tags[1] = HB_OT_TAG_DEFAULT_SCRIPT;
136 else if (script_tags[1] != HB_OT_TAG_DEFAULT_SCRIPT)
137 script_tags[2] = HB_OT_TAG_DEFAULT_SCRIPT;
140 else
142 if (script_tags[1] == HB_OT_TAG_DEFAULT_SCRIPT)
143 script_tags[1] = HB_TAG_NONE;
146 hb_ot_layout_collect_lookups(face,
147 HB_OT_TAG_GSUB,
148 script_tags,
149 NULL,
150 coverage_tags,
151 gsub_lookups);
153 if (hb_set_is_empty(gsub_lookups))
154 goto Exit; /* nothing to do */
156 hb_ot_layout_collect_lookups(face,
157 HB_OT_TAG_GPOS,
158 script_tags,
159 NULL,
160 coverage_tags,
161 gpos_lookups);
163 TA_LOG_GLOBAL(("GSUB lookups (style `%s'):\n"
164 " ",
165 ta_style_names[style_class->style]));
167 #ifdef TA_DEBUG
168 count = 0;
169 #endif
171 for (idx = -1; hb_set_next(gsub_lookups, &idx);)
173 #ifdef TA_DEBUG
174 TA_LOG_GLOBAL((" %d", idx));
175 count++;
176 #endif
178 /* get output coverage of GSUB feature */
179 hb_ot_layout_lookup_collect_glyphs(face,
180 HB_OT_TAG_GSUB,
181 idx,
182 NULL,
183 NULL,
184 NULL,
185 gsub_glyphs);
188 #ifdef TA_DEBUG
189 if (!count)
190 TA_LOG_GLOBAL((" (none)"));
191 TA_LOG_GLOBAL(("\n\n"));
192 #endif
194 TA_LOG_GLOBAL(("GPOS lookups (style `%s'):\n"
195 " ",
196 ta_style_names[style_class->style]));
198 #ifdef TA_DEBUG
199 count = 0;
200 #endif
202 for (idx = -1; hb_set_next(gpos_lookups, &idx);)
204 #ifdef TA_DEBUG
205 TA_LOG_GLOBAL((" %d", idx));
206 count++;
207 #endif
209 /* get input coverage of GPOS feature */
210 hb_ot_layout_lookup_collect_glyphs(face,
211 HB_OT_TAG_GPOS,
212 idx,
213 NULL,
214 gpos_glyphs,
215 NULL,
216 NULL);
219 #ifdef TA_DEBUG
220 if (!count)
221 TA_LOG_GLOBAL((" (none)"));
222 TA_LOG_GLOBAL(("\n\n"));
223 #endif
226 * we now check whether we can construct blue zones, using glyphs
227 * covered by the feature only -- in case there is not a single zone
228 * (this is, not a single character is covered), we skip this coverage
230 if (style_class->coverage != TA_COVERAGE_DEFAULT)
232 TA_Blue_Stringset bss = style_class->blue_stringset;
233 const TA_Blue_StringRec* bs = &ta_blue_stringsets[bss];
235 FT_Bool found = 0;
238 for (; bs->string != TA_BLUE_STRING_MAX; bs++)
240 const char* p = &ta_blue_strings[bs->string];
243 while (*p)
245 hb_codepoint_t ch;
248 GET_UTF8_CHAR(ch, p);
250 for (idx = -1; hb_set_next(gsub_lookups, &idx);)
252 hb_codepoint_t gidx = FT_Get_Char_Index(globals->face, ch);
255 if (hb_ot_layout_lookup_would_substitute(face, idx,
256 &gidx, 1, 1))
258 found = 1;
259 break;
265 if (!found)
267 TA_LOG_GLOBAL((" no blue characters found; style skipped\n"));
268 goto Exit;
273 * Various OpenType features might use the same glyphs at different
274 * vertical positions; for example, superscript and subscript glyphs
275 * could be the same. However, the auto-hinter is completely
276 * agnostic of OpenType features after the feature analysis has been
277 * completed: The engine then simply receives a glyph index and returns a
278 * hinted and usually rendered glyph.
280 * Consider the superscript feature of font `pala.ttf': Some of the
281 * glyphs are `real', this is, they have a zero vertical offset, but
282 * most of them are small caps glyphs shifted up to the superscript
283 * position (this is, the `sups' feature is present in both the GSUB and
284 * GPOS tables). The code for blue zones computation actually uses a
285 * feature's y offset so that the `real' glyphs get correct hints. But
286 * later on it is impossible to decide whether a glyph index belongs to,
287 * say, the small caps or superscript feature.
289 * For this reason, we don't assign a style to a glyph if the current
290 * feature covers the glyph in both the GSUB and the GPOS tables. This
291 * is quite a broad condition, assuming that
293 * (a) glyphs that get used in multiple features are present in a
294 * feature without vertical shift,
296 * and
298 * (b) a feature's GPOS data really moves the glyph vertically.
300 * Not fulfilling condition (a) makes a font larger; it would also
301 * reduce the number of glyphs that could be addressed directly without
302 * using OpenType features, so this assumption is rather strong.
304 * Condition (b) is much weaker, and there might be glyphs which get
305 * missed. However, the OpenType features we are going to handle are
306 * primarily located in GSUB, and HarfBuzz doesn't provide an API to
307 * directly get the necessary information from the GPOS table. A
308 * possible solution might be to directly parse the GPOS table to find
309 * out whether a glyph gets shifted vertically, but this is something I
310 * would like to avoid if not really necessary.
312 * Note that we don't follow this logic for the default coverage.
313 * Complex scripts like Devanagari have mandatory GPOS features to
314 * position many glyph elements, using mark-to-base or mark-to-ligature
315 * tables; the number of glyphs missed due to condition (b) would be far
316 * too large.
318 if (style_class->coverage != TA_COVERAGE_DEFAULT)
319 hb_set_subtract(gsub_glyphs, gpos_glyphs);
321 #ifdef TA_DEBUG
322 TA_LOG_GLOBAL((" glyphs without GPOS data (`*' means already assigned)"));
323 count = 0;
324 #endif
326 for (idx = -1; hb_set_next(gsub_glyphs, &idx);)
328 #ifdef TA_DEBUG
329 if (!(count % 10))
330 TA_LOG_GLOBAL(("\n"
331 " "));
333 TA_LOG_GLOBAL((" %d", idx));
334 count++;
335 #endif
337 /* glyph indices returned by `hb_ot_layout_lookup_collect_glyphs' */
338 /* can be arbitrary: some fonts use fake indices for processing */
339 /* internal to GSUB or GPOS, which is fully valid */
340 if (idx >= (hb_codepoint_t)globals->glyph_count)
341 continue;
343 if (gstyles[idx] == TA_STYLE_UNASSIGNED)
344 gstyles[idx] = (FT_Byte)style_class->style;
345 #ifdef TA_DEBUG
346 else
347 TA_LOG_GLOBAL(("*"));
348 #endif
351 #ifdef TA_DEBUG
352 if (!count)
353 TA_LOG_GLOBAL(("\n"
354 " (none)"));
355 TA_LOG_GLOBAL(("\n\n"));
356 #endif
358 Exit:
359 hb_set_destroy(gsub_lookups);
360 hb_set_destroy(gsub_glyphs);
361 hb_set_destroy(gpos_lookups);
362 hb_set_destroy(gpos_glyphs);
364 return FT_Err_Ok;
368 /* construct HarfBuzz features */
369 #undef COVERAGE
370 #define COVERAGE(name, NAME, description, \
371 tag1, tag2, tag3, tag4) \
372 static const hb_feature_t name ## _feature[] = \
375 HB_TAG(tag1, tag2, tag3, tag4), \
376 1, 0, (unsigned int)-1 \
380 #include "tacover.h"
383 /* define mapping between HarfBuzz features and TA_Coverage */
384 #undef COVERAGE
385 #define COVERAGE(name, NAME, description, \
386 tag1, tag2, tag3, tag4) \
387 name ## _feature,
389 static const hb_feature_t* features[] =
391 #include "tacover.h"
393 NULL /* TA_COVERAGE_DEFAULT */
397 FT_Error
398 ta_get_char_index(TA_StyleMetrics metrics,
399 FT_ULong charcode,
400 FT_ULong *codepoint,
401 FT_Long *y_offset)
403 TA_StyleClass style_class;
405 const hb_feature_t* feature;
407 FT_ULong in_idx, out_idx;
410 if (!metrics)
411 return FT_Err_Invalid_Argument;
413 in_idx = FT_Get_Char_Index(metrics->globals->face, charcode);
415 style_class = metrics->style_class;
417 feature = features[style_class->coverage];
419 if (feature)
421 FT_UInt upem = metrics->globals->face->units_per_EM;
423 hb_font_t* font = metrics->globals->hb_font;
424 hb_buffer_t* buf = hb_buffer_create();
426 uint32_t c = (uint32_t)charcode;
428 hb_glyph_info_t* ginfo;
429 hb_glyph_position_t* gpos;
430 unsigned int gcount;
433 /* we shape at a size of units per EM; this means font units */
434 hb_font_set_scale(font, upem, upem);
436 /* XXX: is this sufficient for a single character of any script? */
437 hb_buffer_set_direction(buf, HB_DIRECTION_LTR);
438 hb_buffer_set_script(buf, scripts[style_class->script]);
440 /* we add one character to `buf' ... */
441 hb_buffer_add_utf32(buf, &c, 1, 0, 1);
443 /* ... and apply one feature */
444 hb_shape(font, buf, feature, 1);
446 ginfo = hb_buffer_get_glyph_infos(buf, &gcount);
447 gpos = hb_buffer_get_glyph_positions(buf, &gcount);
449 out_idx = ginfo[0].codepoint;
451 /* getting the same index indicates no substitution, */
452 /* which means that the glyph isn't available in the feature */
453 if (in_idx == out_idx)
455 *codepoint = 0;
456 *y_offset = 0;
458 else
460 *codepoint = out_idx;
461 *y_offset = gpos[0].y_offset;
464 hb_buffer_destroy(buf);
466 #ifdef TA_DEBUG
467 if (gcount > 1)
468 TA_LOG(("ta_get_char_index:"
469 " input character mapped to multiple glyphs\n"));
470 #endif
472 else
474 *codepoint = in_idx;
475 *y_offset = 0;
478 return FT_Err_Ok;
481 /* end of taharfbuzz.c */