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 */
44 #define COVERAGE(name, NAME, description, \
45 tag1, tag2, tag3, tag4) \
46 static const hb_tag_t name ## _coverage[] = \
48 HB_TAG(tag1, tag2, tag3, tag4), \
55 /* define mapping between coverage tags and TA_Coverage */
57 #define COVERAGE(name, NAME, description, \
58 tag1, tag2, tag3, tag4) \
61 static const hb_tag_t
* coverages
[] =
65 NULL
/* TA_COVERAGE_DEFAULT */
69 /* load HarfBuzz script tags */
71 #define SCRIPT(s, S, d, h, dc) h,
73 static const hb_tag_t scripts
[] =
75 #include "ttfautohint-scripts.h"
80 ta_get_coverage(TA_FaceGlobals globals
,
81 TA_StyleClass style_class
,
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 */
92 const hb_tag_t
* coverage_tags
;
93 hb_tag_t script_tags
[] = { HB_TAG_NONE
,
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
,
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 */
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
;
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
;
142 if (script_tags
[1] == HB_OT_TAG_DEFAULT_SCRIPT
)
143 script_tags
[1] = HB_TAG_NONE
;
146 hb_ot_layout_collect_lookups(face
,
153 if (hb_set_is_empty(gsub_lookups
))
154 goto Exit
; /* nothing to do */
156 hb_ot_layout_collect_lookups(face
,
163 TA_LOG_GLOBAL(("GSUB lookups (style `%s'):\n"
165 ta_style_names
[style_class
->style
]));
171 for (idx
= -1; hb_set_next(gsub_lookups
, &idx
);)
174 TA_LOG_GLOBAL((" %d", idx
));
178 /* get output coverage of GSUB feature */
179 hb_ot_layout_lookup_collect_glyphs(face
,
190 TA_LOG_GLOBAL((" (none)"));
191 TA_LOG_GLOBAL(("\n\n"));
194 TA_LOG_GLOBAL(("GPOS lookups (style `%s'):\n"
196 ta_style_names
[style_class
->style
]));
202 for (idx
= -1; hb_set_next(gpos_lookups
, &idx
);)
205 TA_LOG_GLOBAL((" %d", idx
));
209 /* get input coverage of GPOS feature */
210 hb_ot_layout_lookup_collect_glyphs(face
,
221 TA_LOG_GLOBAL((" (none)"));
222 TA_LOG_GLOBAL(("\n\n"));
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
231 TA_Blue_Stringset bss
= style_class
->blue_stringset
;
232 const TA_Blue_StringRec
* bs
= &ta_blue_stringsets
[bss
];
237 for (; bs
->string
!= TA_BLUE_STRING_MAX
; bs
++)
239 const char* p
= &ta_blue_strings
[bs
->string
];
247 GET_UTF8_CHAR(ch
, p
);
249 for (idx
= -1; hb_set_next(gsub_lookups
, &idx
);)
251 hb_codepoint_t gidx
= FT_Get_Char_Index(globals
->face
, ch
);
254 if (hb_ot_layout_lookup_would_substitute(face
, idx
,
266 TA_LOG_GLOBAL((" no blue characters found; style skipped\n"));
272 * Various OpenType features might use the same glyphs at different
273 * vertical positions; for example, superscript and subscript glyphs
274 * could be the same. However, the auto-hinter is completely
275 * agnostic of OpenType features after the feature analysis has been
276 * completed: The engine then simply receives a glyph index and returns a
277 * hinted and usually rendered glyph.
279 * Consider the superscript feature of font `pala.ttf': Some of the
280 * glyphs are `real', this is, they have a zero vertical offset, but
281 * most of them are small caps glyphs shifted up to the superscript
282 * position (this is, the `sups' feature is present in both the GSUB and
283 * GPOS tables). The code for blue zones computation actually uses a
284 * feature's y offset so that the `real' glyphs get correct hints. But
285 * later on it is impossible to decide whether a glyph index belongs to,
286 * say, the small caps or superscript feature.
288 * For this reason, we don't assign a style to a glyph if the current
289 * feature covers the glyph in both the GSUB and the GPOS tables. This
290 * is quite a broad condition, assuming that
292 * (a) glyphs that get used in multiple features are present in a
293 * feature without vertical shift,
297 * (b) a feature's GPOS data really moves the glyph vertically.
299 * Not fulfilling condition (a) makes a font larger; it would also
300 * reduce the number of glyphs that could be addressed directly without
301 * using OpenType features, so this assumption is rather strong.
303 * Condition (b) is much weaker, and there might be glyphs which get
304 * missed. However, the OpenType features we are going to handle are
305 * primarily located in GSUB, and HarfBuzz doesn't provide an API to
306 * directly get the necessary information from the GPOS table. A
307 * possible solution might be to directly parse the GPOS table to find
308 * out whether a glyph gets shifted vertically, but this is something I
309 * would like to avoid if not really necessary.
311 hb_set_subtract(gsub_glyphs
, gpos_glyphs
);
314 TA_LOG_GLOBAL((" glyphs without GPOS data (`*' means already assigned)"));
318 for (idx
= -1; hb_set_next(gsub_glyphs
, &idx
);)
325 TA_LOG_GLOBAL((" %d", idx
));
329 if (gstyles
[idx
] == TA_STYLE_UNASSIGNED
)
330 gstyles
[idx
] = (FT_Byte
)style_class
->style
;
333 TA_LOG_GLOBAL(("*"));
341 TA_LOG_GLOBAL(("\n\n"));
345 hb_set_destroy(gsub_lookups
);
346 hb_set_destroy(gsub_glyphs
);
347 hb_set_destroy(gpos_lookups
);
348 hb_set_destroy(gpos_glyphs
);
354 /* construct HarfBuzz features */
356 #define COVERAGE(name, NAME, description, \
357 tag1, tag2, tag3, tag4) \
358 static const hb_feature_t name ## _feature[] = \
361 HB_TAG(tag1, tag2, tag3, tag4), \
369 /* define mapping between HarfBuzz features and TA_Coverage */
371 #define COVERAGE(name, NAME, description, \
372 tag1, tag2, tag3, tag4) \
375 static const hb_feature_t
* features
[] =
379 NULL
/* TA_COVERAGE_DEFAULT */
384 ta_get_char_index(TA_StyleMetrics metrics
,
389 TA_StyleClass style_class
;
391 const hb_feature_t
* feature
;
393 FT_ULong in_idx
, out_idx
;
397 return FT_Err_Invalid_Argument
;
399 in_idx
= FT_Get_Char_Index(metrics
->globals
->face
, charcode
);
401 style_class
= metrics
->style_class
;
403 feature
= features
[style_class
->coverage
];
407 FT_UInt upem
= metrics
->globals
->face
->units_per_EM
;
409 hb_font_t
* font
= metrics
->globals
->hb_font
;
410 hb_buffer_t
* buf
= hb_buffer_create();
412 uint32_t c
= (uint32_t)charcode
;
414 hb_glyph_info_t
* ginfo
;
415 hb_glyph_position_t
* gpos
;
419 /* we shape at a size of units per EM; this means font units */
420 hb_font_set_scale(font
, upem
, upem
);
422 /* XXX: is this sufficient for a single character of any script? */
423 hb_buffer_set_direction(buf
, HB_DIRECTION_LTR
);
424 hb_buffer_set_script(buf
, scripts
[style_class
->script
]);
426 /* we add one character to `buf' ... */
427 hb_buffer_add_utf32(buf
, &c
, 1, 0, 1);
429 /* ... and apply one feature */
430 hb_shape(font
, buf
, feature
, 1);
432 ginfo
= hb_buffer_get_glyph_infos(buf
, &gcount
);
433 gpos
= hb_buffer_get_glyph_positions(buf
, &gcount
);
435 out_idx
= ginfo
[0].codepoint
;
437 /* getting the same index indicates no substitution, */
438 /* which means that the glyph isn't available in the feature */
439 if (in_idx
== out_idx
)
446 *codepoint
= out_idx
;
447 *y_offset
= gpos
[0].y_offset
;
450 hb_buffer_destroy(buf
);
454 TA_LOG(("ta_get_char_index:"
455 " input character mapped to multiple glyphs\n"));
467 /* end of taharfbuzz.c */