bool FontPlatformData::hasSpaceInLigaturesOrKerning( TypesettingFeatures features) const { const HarfBuzzFace* hbFace = harfBuzzFace(); if (!hbFace) return false; hb_face_t* face = hbFace->face(); ASSERT(face); OwnPtr<hb_font_t> font = adoptPtr(hbFace->createFont()); ASSERT(font); 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.get(), 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; }
static void test_subset_cff1_noop (void) { hb_face_t *face_abc = hb_test_open_font_file("fonts/SourceSansPro-Regular.abc.otf"); hb_set_t *codepoints = hb_set_create (); hb_face_t *face_abc_subset; hb_set_add (codepoints, 'a'); hb_set_add (codepoints, 'b'); hb_set_add (codepoints, 'c'); face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('C','F','F',' ')); hb_face_destroy (face_abc_subset); hb_face_destroy (face_abc); }
static void test_subset_cff1_seac (void) { hb_face_t *face = hb_test_open_font_file ("fonts/cff1_seac.otf"); hb_face_t *face_subset = hb_test_open_font_file ("fonts/cff1_seac.C0.otf"); hb_face_t *face_test; hb_set_t *codepoints = hb_set_create (); hb_set_add (codepoints, 0xC0); /* Agrave */ face_test = hb_subset_test_create_subset (face, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_subset, face_test, HB_TAG ('C','F','F',' ')); hb_face_destroy (face_test); hb_face_destroy (face_subset); hb_face_destroy (face); }
static void test_subset_hdmx_multiple_device_records (void) { hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.multihdmx.abc.ttf"); hb_face_t *face_a = hb_test_open_font_file ("fonts/Roboto-Regular.multihdmx.a.ttf"); hb_set_t *codepoints = hb_set_create (); hb_face_t *face_abc_subset; hb_set_add (codepoints, 'a'); face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_a, face_abc_subset, HB_TAG ('h','d','m','x')); hb_face_destroy (face_abc_subset); hb_face_destroy (face_abc); hb_face_destroy (face_a); }
static void test_subset_cff1_j (void) { hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf"); hb_face_t *face_41_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,4C2E.otf"); hb_set_t *codepoints = hb_set_create (); hb_face_t *face_41_3041_4c2e_subset; hb_set_add (codepoints, 0x41); hb_set_add (codepoints, 0x4C2E); face_41_3041_4c2e_subset = hb_subset_test_create_subset (face_41_3041_4c2e, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_41_4c2e, face_41_3041_4c2e_subset, HB_TAG ('C','F','F',' ')); hb_face_destroy (face_41_3041_4c2e_subset); hb_face_destroy (face_41_3041_4c2e); hb_face_destroy (face_41_4c2e); }
static void test_post_drops_glyph_names (void) { hb_face_t *face_full = hb_subset_test_open_font ("fonts/Mplus1p-Regular.660E,6975,73E0,5EA6,8F38,6E05.ttf"); hb_face_t *face_subset = hb_subset_test_open_font ("fonts/Mplus1p-Regular.660E.ttf"); hb_face_t *face_full_subset; hb_set_t *codepoints = hb_set_create (); hb_set_add (codepoints, 0x660E); face_full_subset = hb_subset_test_create_subset (face_full, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_subset, face_full_subset, HB_TAG ('p','o','s','t')); hb_face_destroy (face_full_subset); hb_face_destroy (face_full); hb_face_destroy (face_subset); }
static void test_subset_hdmx_simple_subset (void) { hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf"); hb_face_t *face_ac = hb_test_open_font_file ("fonts/Roboto-Regular.ac.ttf"); hb_set_t *codepoints = hb_set_create (); hb_face_t *face_abc_subset; hb_set_add (codepoints, 'a'); hb_set_add (codepoints, 'c'); face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('h','d','m','x')); hb_face_destroy (face_abc_subset); hb_face_destroy (face_abc); hb_face_destroy (face_ac); }
static void test_subset_cff1_expert (void) { hb_face_t *face = hb_test_open_font_file ("fonts/cff1_expert.otf"); hb_face_t *face_subset = hb_test_open_font_file ("fonts/cff1_expert.2D,F6E9,FB00.otf"); hb_set_t *codepoints = hb_set_create (); hb_face_t *face_test; hb_set_add (codepoints, 0x2D); hb_set_add (codepoints, 0xF6E9); hb_set_add (codepoints, 0xFB00); face_test = hb_subset_test_create_subset (face, hb_subset_test_create_input (codepoints)); hb_set_destroy (codepoints); hb_subset_test_check (face_subset, face_test, HB_TAG ('C','F','F',' ')); hb_face_destroy (face_test); hb_face_destroy (face_subset); hb_face_destroy (face); }
static void test_subset_cff1_dotsection (void) { hb_face_t *face = hb_test_open_font_file ("fonts/cff1_dotsect.otf"); hb_face_t *face_subset = hb_test_open_font_file ("fonts/cff1_dotsect.nohints.otf"); hb_set_t *codepoints = hb_set_create (); hb_subset_input_t *input; hb_face_t *face_test; hb_set_add (codepoints, 0x69); /* i */ input = hb_subset_test_create_input (codepoints); hb_subset_input_set_drop_hints (input, true); face_test = hb_subset_test_create_subset (face, input); hb_set_destroy (codepoints); hb_subset_test_check (face_subset, face_test, HB_TAG ('C','F','F',' ')); hb_face_destroy (face_test); hb_face_destroy (face_subset); hb_face_destroy (face); }
static void test_subset_cff1_desubr (void) { hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansPro-Regular.abc.otf"); hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansPro-Regular.ac.nosubrs.otf"); hb_set_t *codepoints = hb_set_create (); hb_subset_input_t *input; hb_face_t *face_abc_subset; hb_set_add (codepoints, 'a'); hb_set_add (codepoints, 'c'); input = hb_subset_test_create_input (codepoints); hb_subset_input_set_desubroutinize (input, true); face_abc_subset = hb_subset_test_create_subset (face_abc, input); hb_set_destroy (codepoints); hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C','F','F',' ')); hb_face_destroy (face_abc_subset); hb_face_destroy (face_abc); hb_face_destroy (face_ac); }
static void test_subset_cff1_j_desubr_strip_hints (void) { hb_face_t *face_41_3041_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,3041,4C2E.otf"); hb_face_t *face_41_4c2e = hb_test_open_font_file ("fonts/SourceHanSans-Regular.41,4C2E.nosubrs.nohints.otf"); hb_set_t *codepoints = hb_set_create (); hb_face_t *face_41_3041_4c2e_subset; hb_subset_input_t *input; hb_set_add (codepoints, 0x41); hb_set_add (codepoints, 0x4C2E); input = hb_subset_test_create_input (codepoints); hb_subset_input_set_drop_hints (input, true); hb_subset_input_set_desubroutinize (input, true); face_41_3041_4c2e_subset = hb_subset_test_create_subset (face_41_3041_4c2e, input); hb_set_destroy (codepoints); hb_subset_test_check (face_41_4c2e, face_41_3041_4c2e_subset, HB_TAG ('C','F','F',' ')); hb_face_destroy (face_41_3041_4c2e_subset); hb_face_destroy (face_41_3041_4c2e); hb_face_destroy (face_41_4c2e); }
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; }