Fix ZWJ not working for Indic fonts

This is a fix for bug 15185229 ZWJ not working in Sinhala and Kannada.

Indic fonts (unlike Arabic) require the entire string, including ZWJ,
to be passed to Harfbuzz; it's not enough for the ZWJ to be present in
the context. The solution is to be "sticky" in font itemization,
continuing to use the same font as long as it has Unicode coverage.

Change-Id: I7673bc56fbda09f1e1a4582e8d88342343b706f1
This commit is contained in:
Raph Levien
2014-05-28 15:38:35 -07:00
parent 7d4090fbe9
commit 1391c37ea9
2 changed files with 25 additions and 27 deletions

View File

@@ -32,8 +32,6 @@ public:
~FontCollection(); ~FontCollection();
const FontFamily* getFamilyForChar(uint32_t ch, FontLanguage lang, int variant) const;
class Run { class Run {
public: public:
// Do copy constructor, assignment, destructor so it can be used in vectors // Do copy constructor, assignment, destructor so it can be used in vectors
@@ -74,6 +72,8 @@ private:
size_t end; size_t end;
}; };
const FontInstance* getInstanceForChar(uint32_t ch, FontLanguage lang, int variant) const;
// static for allocating unique id's // static for allocating unique id's
static uint32_t sNextId; static uint32_t sNextId;

View File

@@ -114,13 +114,8 @@ FontCollection::~FontCollection() {
// 3. If a font matches just language, it gets a score of 2. // 3. If a font matches just language, it gets a score of 2.
// 4. Matching the "compact" or "elegant" variant adds one to the score. // 4. Matching the "compact" or "elegant" variant adds one to the score.
// 5. Highest score wins, with ties resolved to the first font. // 5. Highest score wins, with ties resolved to the first font.
const FontCollection::FontInstance* FontCollection::getInstanceForChar(uint32_t ch,
// Note that we may want to make the selection more dependent on FontLanguage lang, int variant) const {
// context, so for example a sequence of Devanagari, ZWJ, Devanagari
// would get itemized as one run, even though by the rules the ZWJ
// would go to the Latin font.
const FontFamily* FontCollection::getFamilyForChar(uint32_t ch, FontLanguage lang,
int variant) const {
if (ch >= mMaxChar) { if (ch >= mMaxChar) {
return NULL; return NULL;
} }
@@ -128,7 +123,7 @@ const FontFamily* FontCollection::getFamilyForChar(uint32_t ch, FontLanguage lan
#ifdef VERBOSE_DEBUG #ifdef VERBOSE_DEBUG
ALOGD("querying range %d:%d\n", range.start, range.end); ALOGD("querying range %d:%d\n", range.start, range.end);
#endif #endif
FontFamily* bestFamily = NULL; const FontInstance* bestInstance = NULL;
int bestScore = -1; int bestScore = -1;
for (size_t i = range.start; i < range.end; i++) { for (size_t i = range.start; i < range.end; i++) {
const FontInstance* instance = mInstanceVec[i]; const FontInstance* instance = mInstanceVec[i];
@@ -136,7 +131,7 @@ const FontFamily* FontCollection::getFamilyForChar(uint32_t ch, FontLanguage lan
FontFamily* family = instance->mFamily; FontFamily* family = instance->mFamily;
// First font family in collection always matches // First font family in collection always matches
if (mInstances[0].mFamily == family) { if (mInstances[0].mFamily == family) {
return family; return instance;
} }
int score = lang.match(family->lang()) * 2; int score = lang.match(family->lang()) * 2;
if (variant != 0 && variant == family->variant()) { if (variant != 0 && variant == family->variant()) {
@@ -144,18 +139,18 @@ const FontFamily* FontCollection::getFamilyForChar(uint32_t ch, FontLanguage lan
} }
if (score > bestScore) { if (score > bestScore) {
bestScore = score; bestScore = score;
bestFamily = family; bestInstance = instance;
} }
} }
} }
return bestFamily; return bestInstance;
} }
void FontCollection::itemize(const uint16_t *string, size_t string_size, FontStyle style, void FontCollection::itemize(const uint16_t *string, size_t string_size, FontStyle style,
vector<Run>* result) const { vector<Run>* result) const {
FontLanguage lang = style.getLanguage(); FontLanguage lang = style.getLanguage();
int variant = style.getVariant(); int variant = style.getVariant();
const FontFamily* lastFamily = NULL; const FontInstance* lastInstance = NULL;
Run* run = NULL; Run* run = NULL;
int nShorts; int nShorts;
for (size_t i = 0; i < string_size; i += nShorts) { for (size_t i = 0; i < string_size; i += nShorts) {
@@ -168,20 +163,23 @@ void FontCollection::itemize(const uint16_t *string, size_t string_size, FontSty
nShorts = 2; nShorts = 2;
} }
} }
const FontFamily* family = getFamilyForChar(ch, lang, variant); // Continue using existing font as long as it has coverage.
if (i == 0 || family != lastFamily) { if (lastInstance == NULL || !lastInstance->mCoverage->get(ch)) {
Run dummy; const FontInstance* instance = getInstanceForChar(ch, lang, variant);
result->push_back(dummy); if (i == 0 || instance != lastInstance) {
run = &result->back(); Run dummy;
if (family == NULL) { result->push_back(dummy);
run->font = NULL; // maybe we should do something different here run = &result->back();
} else { if (instance == NULL) {
run->font = family->getClosestMatch(style); run->font = NULL; // maybe we should do something different here
// TODO: simplify refcounting (FontCollection lifetime dominates) } else {
run->font->RefLocked(); run->font = instance->mFamily->getClosestMatch(style);
// TODO: simplify refcounting (FontCollection lifetime dominates)
run->font->RefLocked();
}
lastInstance = instance;
run->start = i;
} }
lastFamily = family;
run->start = i;
} }
run->end = i + nShorts; run->end = i + nShorts;
} }