bool FontPlatformData::hasSpaceInLigaturesOrKerning( TypesettingFeatures features) const { HarfBuzzFace* hbFace = harfBuzzFace(); if (!hbFace) return false; hb_font_t* font = hbFace->getScaledFont(); ASSERT(font); hb_face_t* face = hb_font_get_face(font); ASSERT(face); hb_codepoint_t space; // If the space glyph isn't present in the font then each space character // will be rendering using a fallback font, which grantees that it cannot // affect the shape of the preceding word. if (!hb_font_get_glyph(font, spaceCharacter, 0, &space)) return false; if (!hb_ot_layout_has_substitution(face) && !hb_ot_layout_has_positioning(face)) { return false; } bool foundSpaceInTable = false; hb_set_t* glyphs = hb_set_create(); if (features & Kerning) foundSpaceInTable = tableHasSpace(face, glyphs, HB_OT_TAG_GPOS, space); if (!foundSpaceInTable && (features & Ligatures)) foundSpaceInTable = tableHasSpace(face, glyphs, HB_OT_TAG_GSUB, space); hb_set_destroy(glyphs); return foundSpaceInTable; }
long findGraphiteFeatureSettingNamed(XeTeXLayoutEngine engine, uint32_t id, const char* name, int namelength) { long rval = -1; hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); if (grFace != NULL) { const gr_feature_ref* feature = gr_face_find_fref(grFace, id); for (int i = 0; i < gr_fref_n_values(feature); i++) { uint32_t len = 0; uint16_t langID = 0x409; // the first call is to get the length of the string gr_fref_value_label(feature, i, &langID, gr_utf8, &len); char* label = (char*) xmalloc(len); label = (char*) gr_fref_value_label(feature, i, &langID, gr_utf8, &len); if (strncmp(label, name, namelength) == 0) { rval = gr_fref_value(feature, i); gr_label_destroy(label); break; } gr_label_destroy(label); } } return rval; }
hb_tag_t getIndFeature(XeTeXFont font, hb_tag_t script, hb_tag_t language, unsigned int index) { hb_tag_t rval = 0; hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont()); for (int i = 0; i < 2; ++i) { unsigned int scriptIndex, langIndex = 0; hb_tag_t tableTag = i == 0 ? HB_OT_TAG_GSUB : HB_OT_TAG_GPOS; if (hb_ot_layout_table_find_script(face, tableTag, script, &scriptIndex)) { if (hb_ot_layout_script_find_language(face, tableTag, scriptIndex, language, &langIndex) || language == 0) { unsigned int featCount = hb_ot_layout_language_get_feature_tags(face, tableTag, scriptIndex, langIndex, 0, NULL, NULL); hb_tag_t* featList = (hb_tag_t*) xcalloc(featCount, sizeof(hb_tag_t*)); hb_ot_layout_language_get_feature_tags(face, tableTag, scriptIndex, langIndex, 0, &featCount, featList); if (index < featCount) { rval = featList[index]; break; } index -= featCount; } } } return rval; }
bool initGraphiteBreaking(XeTeXLayoutEngine engine, const uint16_t* txtPtr, int txtLen) { hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); gr_font* grFont = hb_graphite2_font_get_gr_font(engine->font->getHbFont()); if (grFace != NULL && grFont != NULL) { if (grSegment != NULL) { gr_seg_destroy(grSegment); grSegment = NULL; grPrevSlot = NULL; } gr_feature_val *grFeatureValues = gr_face_featureval_for_lang (grFace, tag_from_lang(engine->language)); int nFeatures = engine->nFeatures; hb_feature_t *features = engine->features; while (nFeatures--) { const gr_feature_ref *fref = gr_face_find_fref (grFace, features->tag); if (fref) gr_fref_set_feature_value (fref, features->value, grFeatureValues); features++; } grSegment = gr_make_seg(grFont, grFace, engine->script, grFeatureValues, gr_utf16, txtPtr, txtLen, 0); grPrevSlot = gr_seg_first_slot(grSegment); grTextLen = txtLen; return true; } return false; }
static unsigned int getLargerScriptListTable(XeTeXFont font, hb_tag_t** scriptList) { unsigned int rval = 0; hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont()); hb_tag_t* scriptListSub = NULL; hb_tag_t* scriptListPos = NULL; unsigned int scriptCountSub = hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, NULL, NULL); scriptListSub = (hb_tag_t*) xcalloc(scriptCountSub, sizeof(hb_tag_t*)); hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, &scriptCountSub, scriptListSub); unsigned int scriptCountPos = hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GPOS, 0, NULL, NULL); scriptListPos = (hb_tag_t*) xcalloc(scriptCountPos, sizeof(hb_tag_t*)); hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, &scriptCountPos, scriptListPos); if (scriptCountSub > scriptCountPos) { if (scriptList != NULL) *scriptList = scriptListSub; rval = scriptCountSub; } else { if (scriptList != NULL) *scriptList = scriptListPos; rval = scriptCountPos; } return rval; }
hb_blob_t* getFontTable(const MinikinFont* minikinFont, uint32_t tag) { assertMinikinLocked(); hb_font_t* font = getHbFontLocked(minikinFont); hb_face_t* face = hb_font_get_face(font); hb_blob_t* blob = hb_face_reference_table(face, tag); hb_font_destroy(font); return blob; }
uint32_t countGraphiteFeatures(XeTeXLayoutEngine engine) { uint32_t rval = 0; hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); if (grFace != NULL) rval = gr_face_n_fref(grFace); return rval; }
uint32_t getGraphiteFeatureSettingCode(XeTeXLayoutEngine engine, uint32_t featureID, uint32_t index) { uint32_t rval = 0; hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); if (grFace != NULL) { const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID); rval = gr_fref_value(feature, index); } return rval; }
char * getGraphiteFeatureLabel(XeTeXLayoutEngine engine, uint32_t featureID) { hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); if (grFace != NULL) { const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID); uint32_t len = 0; uint16_t langID = 0x409; return (char *) gr_fref_label(feature, &langID, gr_utf8, &len); } return NULL; }
uint32_t getGraphiteFeatureDefaultSetting(XeTeXLayoutEngine engine, uint32_t featureID) { uint32_t rval = 0; hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); if (grFace != NULL) { const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID); gr_feature_val *featureValues = gr_face_featureval_for_lang (grFace, tag_from_lang(engine->language)); rval = gr_fref_feature_value(feature, featureValues); } return rval; }
unsigned int countFeatures(XeTeXFont font, hb_tag_t script, hb_tag_t language) { unsigned int rval = 0; hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont()); for (int i = 0; i < 2; ++i) { unsigned int scriptIndex, langIndex = 0; hb_tag_t tableTag = i == 0 ? HB_OT_TAG_GSUB : HB_OT_TAG_GPOS; if (hb_ot_layout_table_find_script(face, tableTag, script, &scriptIndex)) { if (hb_ot_layout_script_find_language(face, tableTag, scriptIndex, language, &langIndex) || language == 0) { rval += hb_ot_layout_language_get_feature_tags(face, tableTag, scriptIndex, langIndex, 0, NULL, NULL); } } } return rval; }
hb_tag_t getIndLanguage(XeTeXFont font, hb_tag_t script, unsigned int index) { hb_tag_t rval = 0; hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont()); hb_tag_t* scriptList; unsigned int scriptCount = getLargerScriptListTable(font, &scriptList); if (scriptList != NULL) { for (int i = 0; i < scriptCount; i++) { if (scriptList[i] == script) { unsigned int langCount; hb_tag_t* langList; langCount = hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, i, 0, NULL, NULL); langList = (hb_tag_t*) xcalloc(langCount, sizeof(hb_tag_t*)); hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, i, 0, &langCount, langList); if (index < langCount) { rval = langList[index]; break; } free(langList); langCount = hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GPOS, i, 0, NULL, NULL); langList = (hb_tag_t*) xcalloc(langCount, sizeof(hb_tag_t*)); hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GPOS, i, 0, &langCount, langList); if (index < langCount) { rval = langList[index]; break; } free(langList); } } } return rval; }
char * getGraphiteFeatureSettingLabel(XeTeXLayoutEngine engine, uint32_t featureID, uint32_t settingID) { hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); if (grFace != NULL) { const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID); for (int i = 0; i < gr_fref_n_values(feature); i++) { if (settingID == gr_fref_value(feature, i)) { uint32_t len = 0; uint16_t langID = 0x409; return (char *) gr_fref_value_label(feature, i, &langID, gr_utf8, &len); } } } return NULL; }
unsigned int countLanguages(XeTeXFont font, hb_tag_t script) { unsigned int rval = 0; hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont()); hb_tag_t* scriptList; unsigned int scriptCount = getLargerScriptListTable(font, &scriptList); if (scriptList != NULL) { for (int i = 0; i < scriptCount; i++) { if (scriptList[i] == script) { rval += hb_ot_layout_script_get_language_tags (face, HB_OT_TAG_GSUB, i, 0, NULL, NULL); rval += hb_ot_layout_script_get_language_tags (face, HB_OT_TAG_GPOS, i, 0, NULL, NULL); break; } } } return rval; }
const XeTeXFontMgr::OpSizeRec* XeTeXFontMgr::getOpSize(XeTeXFont font) { hb_font_t* hbFont = ((XeTeXFontInst*)font)->getHbFont(); if (hbFont != NULL) { hb_face_t* face = hb_font_get_face(hbFont); OpSizeRec* pSizeRec = (OpSizeRec*) xmalloc(sizeof(OpSizeRec)); bool ok = hb_ot_layout_get_size_params(face, &pSizeRec->designSize, &pSizeRec->subFamilyID, &pSizeRec->nameCode, &pSizeRec->minSize, &pSizeRec->maxSize); if (ok) return pSizeRec; free(pSizeRec); return NULL; } return NULL; }
FT_Error af_get_coverage( AF_FaceGlobals globals, AF_StyleClass style_class, FT_UShort* gstyles ) { hb_face_t* face; hb_set_t* gsub_lookups; /* GSUB lookups for a given script */ hb_set_t* gsub_glyphs; /* glyphs covered by GSUB lookups */ hb_set_t* gpos_lookups; /* GPOS lookups for a given script */ hb_set_t* gpos_glyphs; /* glyphs covered by GPOS lookups */ hb_script_t script; const hb_tag_t* coverage_tags; hb_tag_t script_tags[] = { HB_TAG_NONE, HB_TAG_NONE, HB_TAG_NONE, HB_TAG_NONE }; hb_codepoint_t idx; #ifdef FT_DEBUG_LEVEL_TRACE int count; #endif if ( !globals || !style_class || !gstyles ) return FT_THROW( Invalid_Argument ); face = hb_font_get_face( globals->hb_font ); gsub_lookups = hb_set_create(); gsub_glyphs = hb_set_create(); gpos_lookups = hb_set_create(); gpos_glyphs = hb_set_create(); coverage_tags = coverages[style_class->coverage]; script = scripts[style_class->script]; /* Convert a HarfBuzz script tag into the corresponding OpenType */ /* tag or tags -- some Indic scripts like Devanagari have an old */ /* and a new set of features. */ hb_ot_tags_from_script( script, &script_tags[0], &script_tags[1] ); /* `hb_ot_tags_from_script' usually returns HB_OT_TAG_DEFAULT_SCRIPT */ /* as the second tag. We change that to HB_TAG_NONE except for the */ /* default script. */ if ( style_class->script == globals->module->default_script && style_class->coverage == AF_COVERAGE_DEFAULT ) { if ( script_tags[0] == HB_TAG_NONE ) script_tags[0] = HB_OT_TAG_DEFAULT_SCRIPT; else { if ( script_tags[1] == HB_TAG_NONE ) script_tags[1] = HB_OT_TAG_DEFAULT_SCRIPT; else if ( script_tags[1] != HB_OT_TAG_DEFAULT_SCRIPT ) script_tags[2] = HB_OT_TAG_DEFAULT_SCRIPT; } } else { if ( script_tags[1] == HB_OT_TAG_DEFAULT_SCRIPT ) script_tags[1] = HB_TAG_NONE; } hb_ot_layout_collect_lookups( face, HB_OT_TAG_GSUB, script_tags, NULL, coverage_tags, gsub_lookups ); if ( hb_set_is_empty( gsub_lookups ) ) goto Exit; /* nothing to do */ hb_ot_layout_collect_lookups( face, HB_OT_TAG_GPOS, script_tags, NULL, coverage_tags, gpos_lookups ); FT_TRACE4(( "GSUB lookups (style `%s'):\n" " ", af_style_names[style_class->style] )); #ifdef FT_DEBUG_LEVEL_TRACE count = 0; #endif for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups, &idx ); ) { #ifdef FT_DEBUG_LEVEL_TRACE FT_TRACE4(( " %d", idx )); count++; #endif /* get output coverage of GSUB feature */ hb_ot_layout_lookup_collect_glyphs( face, HB_OT_TAG_GSUB, idx, NULL, NULL, NULL, gsub_glyphs ); } #ifdef FT_DEBUG_LEVEL_TRACE if ( !count ) FT_TRACE4(( " (none)" )); FT_TRACE4(( "\n\n" )); #endif FT_TRACE4(( "GPOS lookups (style `%s'):\n" " ", af_style_names[style_class->style] )); #ifdef FT_DEBUG_LEVEL_TRACE count = 0; #endif for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gpos_lookups, &idx ); ) { #ifdef FT_DEBUG_LEVEL_TRACE FT_TRACE4(( " %d", idx )); count++; #endif /* get input coverage of GPOS feature */ hb_ot_layout_lookup_collect_glyphs( face, HB_OT_TAG_GPOS, idx, NULL, gpos_glyphs, NULL, NULL ); } #ifdef FT_DEBUG_LEVEL_TRACE if ( !count ) FT_TRACE4(( " (none)" )); FT_TRACE4(( "\n\n" )); #endif /* * We now check whether we can construct blue zones, using glyphs * covered by the feature only. In case there is not a single zone * (this is, not a single character is covered), we skip this coverage. * */ if ( style_class->coverage != AF_COVERAGE_DEFAULT ) { AF_Blue_Stringset bss = style_class->blue_stringset; const AF_Blue_StringRec* bs = &af_blue_stringsets[bss]; FT_Bool found = 0; for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ ) { const char* p = &af_blue_strings[bs->string]; while ( *p ) { hb_codepoint_t ch; GET_UTF8_CHAR( ch, p ); for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups, &idx ); ) { hb_codepoint_t gidx = FT_Get_Char_Index( globals->face, ch ); if ( hb_ot_layout_lookup_would_substitute( face, idx, &gidx, 1, 1 ) ) { found = 1; break; } } } } if ( !found ) { FT_TRACE4(( " no blue characters found; style skipped\n" )); goto Exit; } } /* * Various OpenType features might use the same glyphs at different * vertical positions; for example, superscript and subscript glyphs * could be the same. However, the auto-hinter is completely * agnostic of OpenType features after the feature analysis has been * completed: The engine then simply receives a glyph index and returns a * hinted and usually rendered glyph. * * Consider the superscript feature of font `pala.ttf': Some of the * glyphs are `real', this is, they have a zero vertical offset, but * most of them are small caps glyphs shifted up to the superscript * position (this is, the `sups' feature is present in both the GSUB and * GPOS tables). The code for blue zones computation actually uses a * feature's y offset so that the `real' glyphs get correct hints. But * later on it is impossible to decide whether a glyph index belongs to, * say, the small caps or superscript feature. * * For this reason, we don't assign a style to a glyph if the current * feature covers the glyph in both the GSUB and the GPOS tables. This * is quite a broad condition, assuming that * * (a) glyphs that get used in multiple features are present in a * feature without vertical shift, * * and * * (b) a feature's GPOS data really moves the glyph vertically. * * Not fulfilling condition (a) makes a font larger; it would also * reduce the number of glyphs that could be addressed directly without * using OpenType features, so this assumption is rather strong. * * Condition (b) is much weaker, and there might be glyphs which get * missed. However, the OpenType features we are going to handle are * primarily located in GSUB, and HarfBuzz doesn't provide an API to * directly get the necessary information from the GPOS table. A * possible solution might be to directly parse the GPOS table to find * out whether a glyph gets shifted vertically, but this is something I * would like to avoid if not really necessary. * * Note that we don't follow this logic for the default coverage. * Complex scripts like Devanagari have mandatory GPOS features to * position many glyph elements, using mark-to-base or mark-to-ligature * tables; the number of glyphs missed due to condition (b) would be far * too large. * */ if ( style_class->coverage != AF_COVERAGE_DEFAULT ) hb_set_subtract( gsub_glyphs, gpos_glyphs ); #ifdef FT_DEBUG_LEVEL_TRACE FT_TRACE4(( " glyphs without GPOS data (`*' means already assigned)" )); count = 0; #endif for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_glyphs, &idx ); ) { #ifdef FT_DEBUG_LEVEL_TRACE if ( !( count % 10 ) ) FT_TRACE4(( "\n" " " )); FT_TRACE4(( " %d", idx )); count++; #endif /* glyph indices returned by `hb_ot_layout_lookup_collect_glyphs' */ /* can be arbitrary: some fonts use fake indices for processing */ /* internal to GSUB or GPOS, which is fully valid */ if ( idx >= (hb_codepoint_t)globals->glyph_count ) continue; if ( gstyles[idx] == AF_STYLE_UNASSIGNED ) gstyles[idx] = (FT_UShort)style_class->style; #ifdef FT_DEBUG_LEVEL_TRACE else FT_TRACE4(( "*" )); #endif } #ifdef FT_DEBUG_LEVEL_TRACE if ( !count ) FT_TRACE4(( "\n" " (none)" )); FT_TRACE4(( "\n\n" )); #endif Exit: hb_set_destroy( gsub_lookups ); hb_set_destroy( gsub_glyphs ); hb_set_destroy( gpos_lookups ); hb_set_destroy( gpos_glyphs ); return FT_Err_Ok; }
int shape (lua_State *L) { const char * text = luaL_checkstring(L, 1); FT_Face face = lua_touserdata(L, 2); const char * script = luaL_checkstring(L, 3); const char * direction_s = luaL_checkstring(L, 4); const char * lang = luaL_checkstring(L, 5); double point_size = luaL_checknumber(L, 6); const char * featurestring = luaL_checkstring(L, 7); hb_segment_properties_t segment_props; hb_shape_plan_t *shape_plan; hb_direction_t direction; hb_feature_t* features; int nFeatures = 0; unsigned int glyph_count = 0; hb_font_t *hb_ft_font; hb_face_t *hb_ft_face; hb_buffer_t *buf; hb_glyph_info_t *glyph_info; hb_glyph_position_t *glyph_pos; unsigned int j; features = scan_feature_string(featurestring, &nFeatures); if (!strcasecmp(direction_s,"RTL")) direction = HB_DIRECTION_RTL; else if (!strcasecmp(direction_s,"TTB")) direction = HB_DIRECTION_TTB; else direction = HB_DIRECTION_LTR; hb_ft_font = hb_ft_font_create(face, NULL); hb_face_t* hbFace = hb_font_get_face(hb_ft_font); buf = hb_buffer_create(); hb_buffer_add_utf8(buf, text, strlen(text), 0, strlen(text)); hb_buffer_set_script(buf, hb_tag_from_string(script, strlen(script))); hb_buffer_set_direction(buf, direction); hb_buffer_set_language(buf, hb_language_from_string(lang,strlen(lang))); hb_buffer_guess_segment_properties(buf); hb_buffer_get_segment_properties(buf, &segment_props); shape_plan = hb_shape_plan_create_cached(hbFace, &segment_props, features, nFeatures, NULL); int res = hb_shape_plan_execute(shape_plan, hb_ft_font, buf, features, nFeatures); glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); lua_checkstack(L, glyph_count); for (j = 0; j < glyph_count; ++j) { char namebuf[255]; box glyph_extents = { 0.0, 0.0, 0.0 }; calculate_extents(&glyph_extents, glyph_info[j], glyph_pos[j], face, point_size, direction); lua_newtable(L); lua_pushstring(L, "name"); FT_Get_Glyph_Name( face, glyph_info[j].codepoint, namebuf, 255 ); lua_pushstring(L, namebuf); lua_settable(L, -3); if (direction != HB_DIRECTION_TTB) { /* XXX */ if (glyph_pos[j].x_offset) { lua_pushstring(L, "x_offset"); lua_pushnumber(L, glyph_pos[j].x_offset / 64.0); lua_settable(L, -3); } if (glyph_pos[j].y_offset) { lua_pushstring(L, "y_offset"); lua_pushnumber(L, glyph_pos[j].y_offset / 64.0); lua_settable(L, -3); } } lua_pushstring(L, "codepoint"); lua_pushinteger(L, glyph_info[j].codepoint); lua_settable(L, -3); lua_pushstring(L, "width"); lua_pushnumber(L, glyph_extents.width); lua_settable(L, -3); lua_pushstring(L, "height"); lua_pushnumber(L, glyph_extents.height); lua_settable(L, -3); lua_pushstring(L, "depth"); lua_pushnumber(L, glyph_extents.depth); lua_settable(L, -3); } /* Cleanup */ hb_buffer_destroy(buf); hb_font_destroy(hb_ft_font); hb_shape_plan_destroy(shape_plan); free(features); return glyph_count; }
bool OpenTypeCapsSupport::supportsOpenTypeFeature( hb_script_t script, uint32_t tag) const { hb_face_t* face = hb_font_get_face(m_harfBuzzFace->getScaledFont()); ASSERT(face); ASSERT((tag == HB_TAG('s', 'm', 'c', 'p') || tag == HB_TAG('c', '2', 's', 'c') || tag == HB_TAG('p', 'c', 'a', 'p') || tag == HB_TAG('c', '2', 'p', 'c') || tag == HB_TAG('s', 'u', 'p', 's') || tag == HB_TAG('s', 'u', 'b', 's') || tag == HB_TAG('t', 'i', 't', 'l') || tag == HB_TAG('u', 'n', 'i', 'c') || tag == HB_TAG('v', 'e', 'r', 't'))); bool result = false; if (!hb_ot_layout_has_substitution(face)) return false; // Get the OpenType tag(s) that match this script code const size_t kMaxScriptTags = 4; hb_tag_t scriptTags[kMaxScriptTags] = { HB_TAG_NONE, HB_TAG_NONE, HB_TAG_NONE, HB_TAG_NONE }; hb_ot_tags_from_script(static_cast<hb_script_t>(script), &scriptTags[0], &scriptTags[1]); // Replace the first remaining NONE with DEFAULT for (size_t i = 0; i < kMaxScriptTags; ++i) { if (scriptTags[i] == HB_TAG_NONE) { scriptTags[i] = HB_OT_TAG_DEFAULT_SCRIPT; break; } } // Now check for 'smcp' under the first of those scripts that is present const hb_tag_t kGSUB = HB_TAG('G', 'S', 'U', 'B'); for (size_t j = 0; j < kMaxScriptTags; ++j) { if (scriptTags[j] == HB_TAG_NONE) break; unsigned scriptIndex; if (hb_ot_layout_table_find_script(face, kGSUB, scriptTags[j], &scriptIndex)) { if (hb_ot_layout_language_find_feature(face, kGSUB, scriptIndex, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, tag, nullptr)) { result = true; } break; } } return result; }
int layoutChars(XeTeXLayoutEngine engine, uint16_t chars[], int32_t offset, int32_t count, int32_t max, bool rightToLeft) { bool res; hb_script_t script = HB_SCRIPT_INVALID; hb_direction_t direction = HB_DIRECTION_LTR; hb_segment_properties_t segment_props; hb_shape_plan_t *shape_plan; hb_font_t* hbFont = engine->font->getHbFont(); hb_face_t* hbFace = hb_font_get_face(hbFont); if (engine->font->getLayoutDirVertical()) direction = HB_DIRECTION_TTB; else if (rightToLeft) direction = HB_DIRECTION_RTL; script = hb_ot_tag_to_script (engine->script); if (hbUnicodeFuncs == NULL) hbUnicodeFuncs = _get_unicode_funcs(); hb_buffer_reset(engine->hbBuffer); hb_buffer_set_unicode_funcs(engine->hbBuffer, hbUnicodeFuncs); hb_buffer_add_utf16(engine->hbBuffer, chars, max, offset, count); hb_buffer_set_direction(engine->hbBuffer, direction); hb_buffer_set_script(engine->hbBuffer, script); hb_buffer_set_language(engine->hbBuffer, engine->language); hb_buffer_guess_segment_properties(engine->hbBuffer); hb_buffer_get_segment_properties(engine->hbBuffer, &segment_props); if (engine->ShaperList == NULL) { // HarfBuzz gives graphite2 shaper a priority, so that for hybrid // Graphite/OpenType fonts, Graphite will be used. However, pre-0.9999 // XeTeX preferred OpenType over Graphite, so we are doing the same // here for sake of backward compatibility. engine->ShaperList = (char**) xcalloc(4, sizeof(char*)); engine->ShaperList[0] = (char*) "ot"; engine->ShaperList[1] = (char*) "graphite2"; engine->ShaperList[2] = (char*) "fallback"; engine->ShaperList[3] = NULL; } shape_plan = hb_shape_plan_create_cached(hbFace, &segment_props, engine->features, engine->nFeatures, engine->ShaperList); res = hb_shape_plan_execute(shape_plan, hbFont, engine->hbBuffer, engine->features, engine->nFeatures); if (res) { engine->shaper = strdup(hb_shape_plan_get_shaper(shape_plan)); hb_buffer_set_content_type(engine->hbBuffer, HB_BUFFER_CONTENT_TYPE_GLYPHS); } else { // all selected shapers failed, retrying with default // we don't use _cached here as the cached plain will always fail. hb_shape_plan_destroy(shape_plan); shape_plan = hb_shape_plan_create(hbFace, &segment_props, engine->features, engine->nFeatures, NULL); res = hb_shape_plan_execute(shape_plan, hbFont, engine->hbBuffer, engine->features, engine->nFeatures); if (res) { engine->shaper = strdup(hb_shape_plan_get_shaper(shape_plan)); hb_buffer_set_content_type(engine->hbBuffer, HB_BUFFER_CONTENT_TYPE_GLYPHS); } else { fprintf(stderr, "\nERROR: all shapers failed\n"); exit(3); } } hb_shape_plan_destroy(shape_plan); int glyphCount = hb_buffer_get_length(engine->hbBuffer); #ifdef DEBUG char buf[1024]; unsigned int consumed; printf ("shaper: %s\n", engine->shaper); hb_buffer_serialize_flags_t flags = HB_BUFFER_SERIALIZE_FLAGS_DEFAULT; hb_buffer_serialize_format_t format = HB_BUFFER_SERIALIZE_FORMAT_JSON; hb_buffer_serialize_glyphs (engine->hbBuffer, 0, glyphCount, buf, sizeof(buf), &consumed, hbFont, format, flags); if (consumed) printf ("buffer glyphs: %s\n", buf); #endif return glyphCount; }