Fix OTS warning about `maxp.maxSizeOfInstructions`.
[ttfautohint.git] / lib / tashaper.c
blob3cdf21f3deac69f8969d0f7de8afeb2c6d64b48f
1 /* tashaper.c */
3 /*
4 * Copyright (C) 2014-2022 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 "tashaper.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 tag, 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 "ttfautohint-coverages.h"
55 /* define mapping between coverage tags and TA_Coverage */
56 #undef COVERAGE
57 #define COVERAGE(name, NAME, description, \
58 tag, tag1, tag2, tag3, tag4) \
59 name ## _coverage,
61 static const hb_tag_t* coverages[] =
63 #include "ttfautohint-coverages.h"
65 NULL /* TA_COVERAGE_DEFAULT */
69 /* load HarfBuzz script tags */
70 #undef SCRIPT
71 #define SCRIPT(s, S, d, h, H, ss) h,
73 static const hb_script_t scripts[] =
75 #include "ttfautohint-scripts.h"
79 FT_Error
80 ta_shaper_get_coverage(TA_FaceGlobals globals,
81 TA_StyleClass style_class,
82 FT_UShort* gstyles,
83 FT_UInt* sample_glyph,
84 FT_Bool default_script)
86 hb_face_t* face;
88 hb_set_t* gsub_lookups; /* GSUB lookups for a given script */
89 hb_set_t* gsub_glyphs; /* glyphs covered by GSUB lookups */
90 hb_set_t* gpos_lookups; /* GPOS lookups for a given script */
91 hb_set_t* gpos_glyphs; /* glyphs covered by GPOS lookups */
93 hb_script_t script;
94 const hb_tag_t* coverage_tags;
95 hb_tag_t script_tags[] = { HB_TAG_NONE,
96 HB_TAG_NONE,
97 HB_TAG_NONE,
98 HB_TAG_NONE };
100 hb_codepoint_t idx;
101 #ifdef TA_DEBUG
102 int count;
103 #endif
106 if (!globals || !style_class || !gstyles)
107 return FT_Err_Invalid_Argument;
109 face = hb_font_get_face(globals->hb_font);
111 gsub_lookups = hb_set_create();
112 gsub_glyphs = hb_set_create();
113 gpos_lookups = hb_set_create();
114 gpos_glyphs = hb_set_create();
116 coverage_tags = coverages[style_class->coverage];
117 script = scripts[style_class->script];
119 /* convert a HarfBuzz script tag into the corresponding OpenType */
120 /* tag or tags -- some Indic scripts like Devanagari have an old */
121 /* and a new set of features */
123 unsigned int tags_count = 3;
124 hb_tag_t tags[3];
127 hb_ot_tags_from_script_and_language(script,
128 HB_LANGUAGE_INVALID,
129 &tags_count,
130 tags,
131 NULL,
132 NULL);
133 script_tags[0] = tags_count > 0 ? tags[0] : HB_TAG_NONE;
134 script_tags[1] = tags_count > 1 ? tags[1] : HB_TAG_NONE;
135 script_tags[2] = tags_count > 2 ? tags[2] : HB_TAG_NONE;
138 /* If the second tag is HB_OT_TAG_DEFAULT_SCRIPT, change that to */
139 /* HB_TAG_NONE except for the default script. */
140 if (default_script)
142 if (script_tags[0] == HB_TAG_NONE)
143 script_tags[0] = HB_OT_TAG_DEFAULT_SCRIPT;
144 else
146 if (script_tags[1] == HB_TAG_NONE)
147 script_tags[1] = HB_OT_TAG_DEFAULT_SCRIPT;
148 else if (script_tags[1] != HB_OT_TAG_DEFAULT_SCRIPT)
149 script_tags[2] = HB_OT_TAG_DEFAULT_SCRIPT;
152 else
154 /* we use non-standard tags like `khms' for special purposes; */
155 /* HarfBuzz maps them to `DFLT', which we don't want to handle here */
156 if (script_tags[0] == HB_OT_TAG_DEFAULT_SCRIPT)
157 goto Exit;
160 hb_ot_layout_collect_lookups(face,
161 HB_OT_TAG_GSUB,
162 script_tags,
163 NULL,
164 coverage_tags,
165 gsub_lookups);
167 if (hb_set_is_empty(gsub_lookups))
168 goto Exit; /* nothing to do */
170 hb_ot_layout_collect_lookups(face,
171 HB_OT_TAG_GPOS,
172 script_tags,
173 NULL,
174 coverage_tags,
175 gpos_lookups);
177 TA_LOG_GLOBAL(("GSUB lookups (style `%s'):\n"
178 " ",
179 ta_style_names[style_class->style]));
181 #ifdef TA_DEBUG
182 count = 0;
183 #endif
185 for (idx = HB_SET_VALUE_INVALID; hb_set_next(gsub_lookups, &idx);)
187 #ifdef TA_DEBUG
188 TA_LOG_GLOBAL((" %d", idx));
189 count++;
190 #endif
192 /* get output coverage of GSUB feature */
193 hb_ot_layout_lookup_collect_glyphs(face,
194 HB_OT_TAG_GSUB,
195 idx,
196 NULL,
197 NULL,
198 NULL,
199 gsub_glyphs);
202 #ifdef TA_DEBUG
203 if (!count)
204 TA_LOG_GLOBAL((" (none)"));
205 TA_LOG_GLOBAL(("\n\n"));
206 #endif
208 TA_LOG_GLOBAL(("GPOS lookups (style `%s'):\n"
209 " ",
210 ta_style_names[style_class->style]));
212 #ifdef TA_DEBUG
213 count = 0;
214 #endif
216 for (idx = HB_SET_VALUE_INVALID; hb_set_next(gpos_lookups, &idx);)
218 #ifdef TA_DEBUG
219 TA_LOG_GLOBAL((" %d", idx));
220 count++;
221 #endif
223 /* get input coverage of GPOS feature */
224 hb_ot_layout_lookup_collect_glyphs(face,
225 HB_OT_TAG_GPOS,
226 idx,
227 NULL,
228 gpos_glyphs,
229 NULL,
230 NULL);
233 #ifdef TA_DEBUG
234 if (!count)
235 TA_LOG_GLOBAL((" (none)"));
236 TA_LOG_GLOBAL(("\n\n"));
237 #endif
240 * we now check whether we can construct blue zones, using glyphs
241 * covered by the feature only -- in case there is not a single zone
242 * (this is, not a single character is covered), we skip this coverage
244 if (style_class->coverage != TA_COVERAGE_DEFAULT)
246 TA_Blue_Stringset bss = style_class->blue_stringset;
247 const TA_Blue_StringRec* bs = &ta_blue_stringsets[bss];
249 FT_Bool found = 0;
252 for (; bs->string != TA_BLUE_STRING_MAX; bs++)
254 const char* p = &ta_blue_strings[bs->string];
257 while (*p)
259 hb_codepoint_t ch;
262 GET_UTF8_CHAR(ch, p);
264 for (idx = HB_SET_VALUE_INVALID; hb_set_next(gsub_lookups, &idx);)
266 hb_codepoint_t gidx = FT_Get_Char_Index(globals->face, ch);
269 if (hb_ot_layout_lookup_would_substitute(face, idx,
270 &gidx, 1, 1))
272 found = 1;
273 break;
279 if (!found)
281 TA_LOG_GLOBAL((" no blue characters found; style skipped\n"));
282 goto Exit;
287 * Various OpenType features might use the same glyphs at different
288 * vertical positions; for example, superscript and subscript glyphs
289 * could be the same. However, the auto-hinter is completely
290 * agnostic of OpenType features after the feature analysis has been
291 * completed: The engine then simply receives a glyph index and returns a
292 * hinted and usually rendered glyph.
294 * Consider the superscript feature of font `pala.ttf': Some of the
295 * glyphs are `real', this is, they have a zero vertical offset, but
296 * most of them are small caps glyphs shifted up to the superscript
297 * position (this is, the `sups' feature is present in both the GSUB and
298 * GPOS tables). The code for blue zones computation actually uses a
299 * feature's y offset so that the `real' glyphs get correct hints. But
300 * later on it is impossible to decide whether a glyph index belongs to,
301 * say, the small caps or superscript feature.
303 * For this reason, we don't assign a style to a glyph if the current
304 * feature covers the glyph in both the GSUB and the GPOS tables. This
305 * is quite a broad condition, assuming that
307 * (a) glyphs that get used in multiple features are present in a
308 * feature without vertical shift,
310 * and
312 * (b) a feature's GPOS data really moves the glyph vertically.
314 * Not fulfilling condition (a) makes a font larger; it would also
315 * reduce the number of glyphs that could be addressed directly without
316 * using OpenType features, so this assumption is rather strong.
318 * Condition (b) is much weaker, and there might be glyphs which get
319 * missed. However, the OpenType features we are going to handle are
320 * primarily located in GSUB, and HarfBuzz doesn't provide an API to
321 * directly get the necessary information from the GPOS table. A
322 * possible solution might be to directly parse the GPOS table to find
323 * out whether a glyph gets shifted vertically, but this is something I
324 * would like to avoid if not really necessary.
326 * Note that we don't follow this logic for the default coverage.
327 * Complex scripts like Devanagari have mandatory GPOS features to
328 * position many glyph elements, using mark-to-base or mark-to-ligature
329 * tables; the number of glyphs missed due to condition (b) would be far
330 * too large.
332 if (style_class->coverage != TA_COVERAGE_DEFAULT)
333 hb_set_subtract(gsub_glyphs, gpos_glyphs);
335 #ifdef TA_DEBUG
336 TA_LOG_GLOBAL((" glyphs without GPOS data (`*' means already assigned)"));
337 count = 0;
338 #endif
340 for (idx = HB_SET_VALUE_INVALID; hb_set_next(gsub_glyphs, &idx);)
342 #ifdef TA_DEBUG
343 if (!(count % 10))
344 TA_LOG_GLOBAL(("\n"
345 " "));
347 TA_LOG_GLOBAL((" %d", idx));
348 count++;
349 #endif
351 /* glyph indices returned by `hb_ot_layout_lookup_collect_glyphs' */
352 /* can be arbitrary: some fonts use fake indices for processing */
353 /* internal to GSUB or GPOS, which is fully valid */
354 if (idx >= (hb_codepoint_t)globals->glyph_count)
355 continue;
357 if (gstyles[idx] == TA_STYLE_UNASSIGNED)
359 gstyles[idx] = (FT_UShort)style_class->style;
360 if (!*sample_glyph)
361 *sample_glyph = idx;
363 #ifdef TA_DEBUG
364 else
365 TA_LOG_GLOBAL(("*"));
366 #endif
369 #ifdef TA_DEBUG
370 if (!count)
371 TA_LOG_GLOBAL(("\n"
372 " (none)"));
373 TA_LOG_GLOBAL(("\n\n"));
374 #endif
376 Exit:
377 hb_set_destroy(gsub_lookups);
378 hb_set_destroy(gsub_glyphs);
379 hb_set_destroy(gpos_lookups);
380 hb_set_destroy(gpos_glyphs);
382 return FT_Err_Ok;
386 /* construct HarfBuzz features */
387 #undef COVERAGE
388 #define COVERAGE(name, NAME, description, \
389 tag, tag1, tag2, tag3, tag4) \
390 static const hb_feature_t name ## _feature[] = \
393 HB_TAG(tag1, tag2, tag3, tag4), \
394 1, 0, (unsigned int)-1 \
398 #include "ttfautohint-coverages.h"
401 /* define mapping between HarfBuzz features and TA_Coverage */
402 #undef COVERAGE
403 #define COVERAGE(name, NAME, description, \
404 tag, tag1, tag2, tag3, tag4) \
405 name ## _feature,
407 static const hb_feature_t* features[] =
409 #include "ttfautohint-coverages.h"
411 NULL /* TA_COVERAGE_DEFAULT */
415 void*
416 ta_shaper_buf_create(FT_Face face)
418 FT_UNUSED(face);
420 return (void*)hb_buffer_create();
424 void
425 ta_shaper_buf_destroy(FT_Face face,
426 void* buf)
428 FT_UNUSED(face);
430 hb_buffer_destroy((hb_buffer_t*)buf);
434 const char*
435 ta_shaper_get_cluster(const char* p,
436 TA_StyleMetrics metrics,
437 void* buf_,
438 unsigned int* count)
440 TA_StyleClass style_class;
441 const hb_feature_t* feature;
442 FT_Int upem;
443 const char* q;
444 int len;
446 hb_buffer_t* buf = (hb_buffer_t*)buf_;
447 hb_font_t* font;
448 hb_codepoint_t dummy;
451 upem = (FT_Int)metrics->globals->face->units_per_EM;
452 style_class = metrics->style_class;
453 feature = features[style_class->coverage];
455 font = metrics->globals->hb_font;
457 /* we shape at a size of units per EM; this means font units */
458 hb_font_set_scale(font, upem, upem);
460 while (*p == ' ')
461 p++;
463 /* count bytes up to next space (or end of buffer) */
464 q = p;
465 while (!(*q == ' ' || *q == '\0'))
466 GET_UTF8_CHAR(dummy, q);
467 len = (int)(q - p);
469 /* feed character(s) to the HarfBuzz buffer */
470 hb_buffer_clear_contents(buf);
471 hb_buffer_add_utf8(buf, p, len, 0, len);
473 /* we let HarfBuzz guess the script and writing direction */
474 hb_buffer_guess_segment_properties(buf);
476 /* shape buffer, which means conversion from character codes to */
477 /* glyph indices, possibly applying a feature */
478 hb_shape(font, buf, feature, feature ? 1 : 0);
480 if (feature)
482 hb_buffer_t* hb_buf = metrics->globals->hb_buf;
484 unsigned int gcount;
485 hb_glyph_info_t* ginfo;
487 unsigned int hb_gcount;
488 hb_glyph_info_t* hb_ginfo;
491 /* we have to check whether applying a feature does actually change */
492 /* glyph indices; otherwise the affected glyph or glyphs aren't */
493 /* available at all in the feature */
495 hb_buffer_clear_contents(hb_buf);
496 hb_buffer_add_utf8(hb_buf, p, len, 0, len);
497 hb_buffer_guess_segment_properties(hb_buf);
498 hb_shape(font, hb_buf, NULL, 0);
500 ginfo = hb_buffer_get_glyph_infos(buf, &gcount);
501 hb_ginfo = hb_buffer_get_glyph_infos(hb_buf, &hb_gcount);
503 if (gcount == hb_gcount)
505 unsigned int i;
508 for (i = 0; i < gcount; i++)
509 if (ginfo[i].codepoint != hb_ginfo[i].codepoint)
510 break;
512 if (i == gcount)
514 /* both buffers have identical glyph indices */
515 hb_buffer_clear_contents(buf);
520 *count = hb_buffer_get_length(buf);
522 #ifdef TA_DEBUG
523 if (feature && *count > 1)
524 TA_LOG(("ta_shaper_get_cluster:"
525 " input character mapped to multiple glyphs\n"));
526 #endif
528 return q;
532 FT_ULong
533 ta_shaper_get_elem(TA_StyleMetrics metrics,
534 void* buf_,
535 unsigned int idx,
536 FT_Long* advance,
537 FT_Long* y_offset)
539 hb_buffer_t* buf = (hb_buffer_t*)buf_;
540 hb_glyph_info_t* ginfo;
541 hb_glyph_position_t* gpos;
542 unsigned int gcount;
544 FT_UNUSED(metrics);
547 ginfo = hb_buffer_get_glyph_infos(buf, &gcount);
548 gpos = hb_buffer_get_glyph_positions(buf, &gcount);
550 if (idx >= gcount)
551 return 0;
553 if (advance)
554 *advance = gpos[idx].x_advance;
555 if (y_offset)
556 *y_offset = gpos[idx].y_offset;
558 return ginfo[idx].codepoint;
561 /* end of tashaper.c */