Bug 761442 - treat substitution and positioning lookups involving <space> differently. r=jkew

This commit is contained in:
John Daggett 2013-04-11 22:49:58 +09:00
Родитель a0a8034fb1
Коммит 04b7882c04
3 изменённых файлов: 339 добавлений и 32 удалений

Просмотреть файл

@ -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