зеркало из https://github.com/mozilla/gecko-dev.git
Bug 761442 - treat substitution and positioning lookups involving <space> differently. r=jkew
This commit is contained in:
Родитель
a0a8034fb1
Коммит
04b7882c04
|
@ -1453,6 +1453,7 @@ gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
|
|||
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
|
||||
++gFontCount;
|
||||
#endif
|
||||
mKerningSet = HasFeatureSet(HB_TAG('k','e','r','n'), mKerningEnabled);
|
||||
}
|
||||
|
||||
gfxFont::~gfxFont()
|
||||
|
@ -1501,44 +1502,289 @@ HBGetTable(hb_face_t *face, hb_tag_t aTag, void *aUserData)
|
|||
}
|
||||
|
||||
static bool
|
||||
HasLayoutFeatureInvolving(hb_face_t *aFace, hb_tag_t aTag, uint16_t aGlyph)
|
||||
HasLookupRuleWithGlyphByScript(hb_face_t *aFace, hb_tag_t aTableTag,
|
||||
hb_tag_t aScript, uint16_t aGlyph)
|
||||
{
|
||||
hb_set_t *lookups = hb_set_create();
|
||||
hb_set_t *glyphs = hb_set_create();
|
||||
hb_tag_t scripts[2] = {0};
|
||||
scripts[0] = aScript;
|
||||
|
||||
hb_ot_layout_collect_lookups(aFace, aTag, nullptr, nullptr, nullptr,
|
||||
bool result = false;
|
||||
hb_ot_layout_collect_lookups(aFace, aTableTag, scripts, nullptr, nullptr,
|
||||
lookups);
|
||||
|
||||
hb_codepoint_t index = -1;
|
||||
while (hb_set_next(lookups, &index)) {
|
||||
hb_ot_layout_lookup_collect_glyphs(aFace, aTag, index,
|
||||
hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
|
||||
glyphs, glyphs, glyphs,
|
||||
glyphs);
|
||||
if (hb_set_has(glyphs, aGlyph)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool result = hb_set_has(glyphs, aGlyph);
|
||||
|
||||
hb_set_destroy(glyphs);
|
||||
hb_set_destroy(lookups);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
static void
|
||||
CollectLookupsByFeature(hb_face_t *aFace, hb_tag_t aTableTag,
|
||||
uint32_t aFeatureIndex, hb_set_t *aLookups)
|
||||
{
|
||||
uint32_t lookups[32];
|
||||
uint32_t i, len, offset;
|
||||
|
||||
offset = 0;
|
||||
do {
|
||||
len = ArrayLength(lookups);
|
||||
hb_ot_layout_feature_get_lookups(aFace, aTableTag, aFeatureIndex,
|
||||
offset, &len, lookups);
|
||||
for (i = 0; i < len; i++) {
|
||||
hb_set_add(aLookups, lookups[i]);
|
||||
}
|
||||
offset += len;
|
||||
} while (len == ArrayLength(lookups));
|
||||
}
|
||||
|
||||
static void
|
||||
CollectLookupsByLanguage(hb_face_t *aFace, hb_tag_t aTableTag,
|
||||
hb_tag_t aExcludeFeature,
|
||||
hb_set_t *aLookups, hb_set_t *aExcludedFeatureLookups,
|
||||
uint32_t aScriptIndex, uint32_t aLangIndex)
|
||||
{
|
||||
uint32_t reqFeatureIndex;
|
||||
if (hb_ot_layout_language_get_required_feature_index(aFace, aTableTag,
|
||||
aScriptIndex,
|
||||
aLangIndex,
|
||||
&reqFeatureIndex)) {
|
||||
CollectLookupsByFeature(aFace, aTableTag, reqFeatureIndex, aLookups);
|
||||
}
|
||||
|
||||
uint32_t featureIndexes[32];
|
||||
uint32_t i, len, offset;
|
||||
|
||||
offset = 0;
|
||||
do {
|
||||
len = ArrayLength(featureIndexes);
|
||||
hb_ot_layout_language_get_feature_indexes(aFace, aTableTag,
|
||||
aScriptIndex, aLangIndex,
|
||||
offset, &len, featureIndexes);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
uint32_t featureIndex = featureIndexes[i];
|
||||
|
||||
// get the feature tag
|
||||
hb_tag_t featureTag;
|
||||
uint32_t tagLen = 1;
|
||||
hb_ot_layout_language_get_feature_tags(aFace, aTableTag,
|
||||
aScriptIndex, aLangIndex,
|
||||
offset + i, &tagLen,
|
||||
&featureTag);
|
||||
|
||||
// collect lookups
|
||||
hb_set_t *lookups = featureTag == aExcludeFeature ?
|
||||
aExcludedFeatureLookups : aLookups;
|
||||
CollectLookupsByFeature(aFace, aTableTag, featureIndex, lookups);
|
||||
}
|
||||
offset += len;
|
||||
} while (len == ArrayLength(featureIndexes));
|
||||
}
|
||||
|
||||
static void
|
||||
HasLookupRuleWithGlyph(hb_face_t *aFace, hb_tag_t aTableTag, bool& aHasGlyph,
|
||||
hb_tag_t aExcludeFeature, bool& aHasGlyphExcluded,
|
||||
uint16_t aGlyph)
|
||||
{
|
||||
// iterate over the scripts in the font
|
||||
uint32_t numScripts, numLangs, script, lang;
|
||||
hb_set_t *lookups = hb_set_create();
|
||||
hb_set_t *excludedFeatureLookups = hb_set_create();
|
||||
|
||||
numScripts = hb_ot_layout_table_get_script_tags(aFace, aTableTag, 0,
|
||||
nullptr, nullptr);
|
||||
|
||||
for (script = 0; script < numScripts; script++) {
|
||||
// default lang
|
||||
CollectLookupsByLanguage(aFace, aTableTag, aExcludeFeature,
|
||||
lookups, excludedFeatureLookups,
|
||||
script, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
|
||||
|
||||
// iterate over langs
|
||||
numLangs = hb_ot_layout_script_get_language_tags(aFace, HB_OT_TAG_GPOS,
|
||||
script, 0,
|
||||
nullptr, nullptr);
|
||||
for (lang = 0; lang < numLangs; lang++) {
|
||||
CollectLookupsByLanguage(aFace, aTableTag, aExcludeFeature,
|
||||
lookups, excludedFeatureLookups,
|
||||
script, lang);
|
||||
}
|
||||
}
|
||||
|
||||
// look for the glyph among non-excluded lookups
|
||||
hb_set_t *glyphs = hb_set_create();
|
||||
hb_codepoint_t index = -1;
|
||||
while (hb_set_next(lookups, &index)) {
|
||||
hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
|
||||
glyphs, glyphs, glyphs,
|
||||
glyphs);
|
||||
if (hb_set_has(glyphs, aGlyph)) {
|
||||
aHasGlyph = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// look for the glyph among excluded lookups
|
||||
hb_set_clear(glyphs);
|
||||
index = -1;
|
||||
while (hb_set_next(excludedFeatureLookups, &index)) {
|
||||
hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
|
||||
glyphs, glyphs, glyphs,
|
||||
glyphs);
|
||||
if (hb_set_has(glyphs, aGlyph)) {
|
||||
aHasGlyphExcluded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hb_set_destroy(glyphs);
|
||||
hb_set_destroy(excludedFeatureLookups);
|
||||
hb_set_destroy(lookups);
|
||||
}
|
||||
|
||||
nsDataHashtable<nsUint32HashKey, int32_t> gfxFont::sScriptTagToCode;
|
||||
|
||||
void
|
||||
gfxFont::CheckForFeaturesInvolvingSpace()
|
||||
{
|
||||
mFontEntry->mHasSpaceFeaturesInitialized = true;
|
||||
|
||||
bool result = false;
|
||||
|
||||
hb_face_t *face = hb_face_create_for_tables(HBGetTable, this, nullptr);
|
||||
|
||||
bool result = (hb_ot_layout_has_substitution(face) &&
|
||||
HasLayoutFeatureInvolving(face, HB_OT_TAG_GSUB,
|
||||
GetSpaceGlyph())) ||
|
||||
(hb_ot_layout_has_positioning(face) &&
|
||||
HasLayoutFeatureInvolving(face, HB_OT_TAG_GPOS,
|
||||
GetSpaceGlyph()));
|
||||
uint32_t i, len, offset;
|
||||
uint32_t spaceGlyph = GetSpaceGlyph();
|
||||
int32_t s;
|
||||
|
||||
mFontEntry->mHasSpaceFeaturesSubDefault = false;
|
||||
|
||||
// GSUB lookups - examine per script
|
||||
if (hb_ot_layout_has_substitution(face)) {
|
||||
|
||||
// set up the script ==> code hashtable if needed
|
||||
if (!sScriptTagToCode.IsInitialized()) {
|
||||
sScriptTagToCode.Init(MOZ_NUM_SCRIPT_CODES);
|
||||
for (s = MOZ_SCRIPT_ARABIC; s < MOZ_NUM_SCRIPT_CODES; s++) {
|
||||
hb_script_t scriptTag = hb_script_t(GetScriptTagForCode(s));
|
||||
hb_tag_t s1, s2;
|
||||
hb_ot_tags_from_script(scriptTag, &s1, &s2);
|
||||
sScriptTagToCode.Put(s1, s);
|
||||
if (s2 != HB_OT_TAG_DEFAULT_SCRIPT) {
|
||||
sScriptTagToCode.Put(s2, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over the scripts in the font
|
||||
hb_tag_t scriptTags[8];
|
||||
|
||||
offset = 0;
|
||||
do {
|
||||
len = ArrayLength(scriptTags);
|
||||
hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, offset,
|
||||
&len, scriptTags);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (HasLookupRuleWithGlyphByScript(face, HB_OT_TAG_GSUB,
|
||||
scriptTags[i], spaceGlyph))
|
||||
{
|
||||
result = true;
|
||||
if (scriptTags[i] == HB_TAG('D','F','L','T')) {
|
||||
mFontEntry->mHasSpaceFeaturesSubDefault = true;
|
||||
}
|
||||
if (sScriptTagToCode.Get(scriptTags[i], &s)) {
|
||||
uint32_t index = s >> 5;
|
||||
uint32_t bit = s & 0x1f;
|
||||
mFontEntry->mHasSpaceFeaturesSub[index] |= (1 << bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
offset += len;
|
||||
} while (len == ArrayLength(scriptTags));
|
||||
}
|
||||
|
||||
// GPOS lookups - distinguish kerning from non-kerning features
|
||||
mFontEntry->mHasSpaceFeaturesKerning = false;
|
||||
mFontEntry->mHasSpaceFeaturesNonKerning = false;
|
||||
|
||||
if (hb_ot_layout_has_positioning(face)) {
|
||||
bool hasKerning = false, hasNonKerning = false;
|
||||
HasLookupRuleWithGlyph(face, HB_OT_TAG_GPOS, hasNonKerning,
|
||||
HB_TAG('k','e','r','n'), hasKerning, spaceGlyph);
|
||||
if (hasKerning || hasNonKerning) {
|
||||
result = true;
|
||||
}
|
||||
mFontEntry->mHasSpaceFeaturesKerning = hasKerning;
|
||||
mFontEntry->mHasSpaceFeaturesNonKerning = hasNonKerning;
|
||||
}
|
||||
|
||||
hb_face_destroy(face);
|
||||
mFontEntry->mHasSpaceFeatures = result;
|
||||
|
||||
return result;
|
||||
#ifdef DEBUG_SPACE_LOOKUPS
|
||||
printf("font: %s - subst: %8.8x %8.8x %8.8x %8.8x "
|
||||
"default: %s kerning: %s non-kerning: %s\n",
|
||||
NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
|
||||
mFontEntry->mHasSpaceFeaturesSub[0],
|
||||
mFontEntry->mHasSpaceFeaturesSub[1],
|
||||
mFontEntry->mHasSpaceFeaturesSub[2],
|
||||
mFontEntry->mHasSpaceFeaturesSub[3],
|
||||
(mFontEntry->mHasSpaceFeaturesSubDefault ? "true" : "false"),
|
||||
(mFontEntry->mHasSpaceFeaturesKerning ? "true" : "false"),
|
||||
(mFontEntry->mHasSpaceFeaturesNonKerning ? "true" : "false")
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn)
|
||||
{
|
||||
aFeatureOn = false;
|
||||
|
||||
if (mStyle.featureSettings.IsEmpty() &&
|
||||
GetFontEntry()->mFeatureSettings.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// add feature values from font
|
||||
bool featureSet = false;
|
||||
uint32_t i, count;
|
||||
|
||||
nsTArray<gfxFontFeature>& fontFeatures = GetFontEntry()->mFeatureSettings;
|
||||
count = fontFeatures.Length();
|
||||
for (i = 0; i < count; i++) {
|
||||
const gfxFontFeature& feature = fontFeatures.ElementAt(i);
|
||||
if (feature.mTag == aFeature) {
|
||||
featureSet = true;
|
||||
aFeatureOn = (feature.mValue != 0);
|
||||
}
|
||||
}
|
||||
|
||||
// add feature values from style rules
|
||||
nsTArray<gfxFontFeature>& styleFeatures = mStyle.featureSettings;
|
||||
count = styleFeatures.Length();
|
||||
for (i = 0; i < count; i++) {
|
||||
const gfxFontFeature& feature = styleFeatures.ElementAt(i);
|
||||
if (feature.mTag == aFeature) {
|
||||
featureSet = true;
|
||||
aFeatureOn = (feature.mValue != 0);
|
||||
}
|
||||
}
|
||||
|
||||
return featureSet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2759,8 +3005,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
|
|||
return true;
|
||||
}
|
||||
|
||||
uint32_t flags = aTextRun->GetFlags();
|
||||
if (BypassShapedWordCache(flags)) {
|
||||
if (BypassShapedWordCache(aRunScript)) {
|
||||
return ShapeTextWithoutWordCache(aContext, aString + aRunStart,
|
||||
aRunStart, aRunLength, aRunScript,
|
||||
aTextRun);
|
||||
|
@ -2769,6 +3014,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
|
|||
InitWordCache();
|
||||
|
||||
// the only flags we care about for ShapedWord construction/caching
|
||||
uint32_t flags = aTextRun->GetFlags();
|
||||
flags &= (gfxTextRunFactory::TEXT_IS_RTL |
|
||||
gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES);
|
||||
if (sizeof(T) == sizeof(uint8_t)) {
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "mozilla/gfx/Types.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include <algorithm>
|
||||
#include "nsUnicodeProperties.h"
|
||||
|
||||
typedef struct _cairo_scaled_font cairo_scaled_font_t;
|
||||
|
||||
|
@ -215,15 +216,21 @@ public:
|
|||
mIgnoreGDEF(false),
|
||||
mIgnoreGSUB(false),
|
||||
mSVGInitialized(false),
|
||||
mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
|
||||
mHasSpaceFeaturesInitialized(false),
|
||||
mHasSpaceFeatures(false),
|
||||
mHasSpaceFeaturesKerning(false),
|
||||
mHasSpaceFeaturesNonKerning(false),
|
||||
mHasSpaceFeaturesSubDefault(false),
|
||||
mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
|
||||
mCheckedForGraphiteTables(false),
|
||||
mHasCmapTable(false),
|
||||
mUVSOffset(0), mUVSData(nullptr),
|
||||
mUserFontData(nullptr),
|
||||
mSVGGlyphs(nullptr),
|
||||
mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE)
|
||||
{ }
|
||||
{
|
||||
memset(&mHasSpaceFeaturesSub, 0, sizeof(mHasSpaceFeaturesSub));
|
||||
}
|
||||
|
||||
virtual ~gfxFontEntry();
|
||||
|
||||
|
@ -345,13 +352,18 @@ public:
|
|||
bool mIgnoreGDEF : 1;
|
||||
bool mIgnoreGSUB : 1;
|
||||
bool mSVGInitialized : 1;
|
||||
bool mHasSpaceFeaturesInitialized : 1;
|
||||
bool mHasSpaceFeatures : 1;
|
||||
bool mHasSpaceFeaturesKerning : 1;
|
||||
bool mHasSpaceFeaturesNonKerning : 1;
|
||||
bool mHasSpaceFeaturesSubDefault : 1;
|
||||
|
||||
// bitvector of substitution space features per script
|
||||
uint32_t mHasSpaceFeaturesSub[(MOZ_NUM_SCRIPT_CODES + 31) / 32];
|
||||
|
||||
uint16_t mWeight;
|
||||
int16_t mStretch;
|
||||
|
||||
bool mHasSpaceFeatures;
|
||||
bool mHasSpaceFeaturesInitialized;
|
||||
|
||||
bool mHasGraphiteTables;
|
||||
bool mCheckedForGraphiteTables;
|
||||
bool mHasCmapTable;
|
||||
|
@ -382,8 +394,12 @@ protected:
|
|||
mIgnoreGDEF(false),
|
||||
mIgnoreGSUB(false),
|
||||
mSVGInitialized(false),
|
||||
mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
|
||||
mHasSpaceFeaturesInitialized(false),
|
||||
mHasSpaceFeatures(false),
|
||||
mHasSpaceFeaturesKerning(false),
|
||||
mHasSpaceFeaturesNonKerning(false),
|
||||
mHasSpaceFeaturesSubDefault(false),
|
||||
mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
|
||||
mCheckedForGraphiteTables(false),
|
||||
mHasCmapTable(false),
|
||||
mUVSOffset(0), mUVSData(nullptr),
|
||||
|
@ -1566,24 +1582,49 @@ public:
|
|||
{ return gfxPlatform::GetPlatform()->GetScaledFontForFont(aTarget, this); }
|
||||
|
||||
protected:
|
||||
bool BypassShapedWordCache(uint32_t aRunFlags) {
|
||||
// When creating a textrun in optimizeSpeed mode, we always use the
|
||||
// word cache (which means we do not support features that span
|
||||
// inter-word spaces)
|
||||
if (aRunFlags & gfxTextRunFactory::TEXT_OPTIMIZE_SPEED) {
|
||||
|
||||
bool HasSubstitutionRulesWithSpaceLookups(int32_t aRunScript) {
|
||||
NS_ASSERTION(GetFontEntry()->mHasSpaceFeaturesInitialized,
|
||||
"need to initialize space lookup flags");
|
||||
NS_ASSERTION(aRunScript < MOZ_NUM_SCRIPT_CODES, "weird script code");
|
||||
if (aRunScript == MOZ_SCRIPT_INVALID ||
|
||||
aRunScript >= MOZ_NUM_SCRIPT_CODES) {
|
||||
return false;
|
||||
}
|
||||
uint32_t index = aRunScript >> 5;
|
||||
uint32_t bit = aRunScript & 0x1f;
|
||||
return (mFontEntry->mHasSpaceFeaturesSub[index] & (1 << bit)) != 0;
|
||||
}
|
||||
|
||||
bool BypassShapedWordCache(int32_t aRunScript) {
|
||||
// We record the presence of space-dependent features in the font entry
|
||||
// so that subsequent instantiations for the same font face won't
|
||||
// require us to re-check the tables; however, the actual check is done
|
||||
// by gfxFont because not all font entry subclasses know how to create
|
||||
// a harfbuzz face for introspection.
|
||||
gfxFontEntry *fe = GetFontEntry();
|
||||
if (!fe->mHasSpaceFeaturesInitialized) {
|
||||
fe->mHasSpaceFeaturesInitialized = true;
|
||||
fe->mHasSpaceFeatures = CheckForFeaturesInvolvingSpace();
|
||||
if (!mFontEntry->mHasSpaceFeaturesInitialized) {
|
||||
CheckForFeaturesInvolvingSpace();
|
||||
}
|
||||
return fe->mHasSpaceFeatures;
|
||||
|
||||
if (!mFontEntry->mHasSpaceFeatures) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if font has substitution rules or non-kerning positioning rules
|
||||
// that involve spaces, bypass
|
||||
if (HasSubstitutionRulesWithSpaceLookups(aRunScript) ||
|
||||
mFontEntry->mHasSpaceFeaturesNonKerning ||
|
||||
mFontEntry->mHasSpaceFeaturesSubDefault) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if kerning explicitly enabled/disabled via font-feature-settings or
|
||||
// font-kerning and kerning rules use spaces, only bypass when enabled
|
||||
if (mKerningSet && mFontEntry->mHasSpaceFeaturesKerning) {
|
||||
return mKerningEnabled;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// For 8-bit text, expand to 16-bit and then call the following method.
|
||||
|
@ -1642,7 +1683,13 @@ protected:
|
|||
int32_t aScript,
|
||||
gfxTextRun *aTextRun);
|
||||
|
||||
bool CheckForFeaturesInvolvingSpace();
|
||||
void CheckForFeaturesInvolvingSpace();
|
||||
|
||||
// whether a given feature is included in feature settings from both the
|
||||
// font and the style. aFeatureOn set if resolved feature value is non-zero
|
||||
bool HasFeatureSet(uint32_t aFeature, bool& aFeatureOn);
|
||||
|
||||
static nsDataHashtable<nsUint32HashKey, int32_t> sScriptTagToCode;
|
||||
|
||||
nsRefPtr<gfxFontEntry> mFontEntry;
|
||||
|
||||
|
@ -1735,6 +1782,9 @@ protected:
|
|||
// by the platform
|
||||
bool mApplySyntheticBold;
|
||||
|
||||
bool mKerningSet; // kerning explicitly set?
|
||||
bool mKerningEnabled; // if set, on or off?
|
||||
|
||||
nsExpirationState mExpirationState;
|
||||
gfxFontStyle mStyle;
|
||||
nsAutoTArray<gfxGlyphExtents*,1> mGlyphExtentsArray;
|
||||
|
|
|
@ -545,13 +545,24 @@ hb_font_set_ppem
|
|||
hb_font_set_scale
|
||||
hb_language_from_string
|
||||
hb_ot_layout_collect_lookups
|
||||
hb_ot_layout_feature_get_lookups
|
||||
hb_ot_layout_has_positioning
|
||||
hb_ot_layout_has_substitution
|
||||
hb_ot_layout_language_get_feature_indexes
|
||||
hb_ot_layout_language_get_feature_tags
|
||||
hb_ot_layout_language_get_required_feature_index
|
||||
hb_ot_layout_lookup_collect_glyphs
|
||||
hb_ot_layout_script_get_language_tags
|
||||
hb_ot_layout_table_choose_script
|
||||
hb_ot_layout_table_get_script_tags
|
||||
hb_ot_tag_to_language
|
||||
hb_ot_tags_from_script
|
||||
hb_set_add
|
||||
hb_set_clear
|
||||
hb_set_create
|
||||
hb_set_destroy
|
||||
hb_set_has
|
||||
hb_set_is_empty
|
||||
hb_set_next
|
||||
hb_shape
|
||||
hb_unicode_funcs_create
|
||||
|
|
Загрузка…
Ссылка в новой задаче