/////////////////////////////////////////////////////////////////////////////// // AppendThaiCharCluster // eastl_size_t Typesetter::AppendThaiCharCluster(eastl_size_t iCharBegin, eastl_size_t charCount, const Char* pCharCluster, eastl_size_t clusterSize) { // The 'iCharBegin' argument refers to the position of pCharCluster in mLineLayout.mCharArray. // There is a possibility that pCharCluster doesn't point to mLineLayout.mCharArray[iCharBegin], // as we may be doing some kind of implicit substitution. Thus we have pCharCluster // as an explicit parameter. const AnalysisInfo* const pAnalysisInfo = &mLineLayout.mAnalysisInfoArray[iCharBegin]; GlyphId pGlyphIdArray[kMaxThaiGlyphClusterSize]; eastl_size_t glyphCount = 0; EA_ASSERT(clusterSize && ((iCharBegin + clusterSize) <= mLineLayout.mCharArray.size())); if(IsCharThaiLao(*pCharCluster)) // If the cluster is using Thai or Lao script... glyphCount = GetThaiGlyphs(iCharBegin, pCharCluster, clusterSize, pGlyphIdArray); else { // In this case we choose glyphs as if doing general glyph shaping. // We just convert the Unicode Chars to glyphs. for(eastl_size_t i = 0; i < clusterSize; ) i += GetGlyphsForChar(pCharCluster + i, clusterSize - i, &mLineLayout.mAnalysisInfoArray[i], pGlyphIdArray + glyphCount, glyphCount); } AppendGeneralGlyphCluster(iCharBegin, charCount, pCharCluster, charCount, pGlyphIdArray, glyphCount, pAnalysisInfo->mnBidiLevel); PlaceGeneralGlyphCluster(iCharBegin, charCount); // As it stands now, all chars must result in at least one glyph, though that // one glyph could be a zero-width space character. Note that the render code // has the opportunity to remove such a "no-op" instruction from the display list. EA_ASSERT(glyphCount > 0); return glyphCount; }
// ShapeArabic // void Typesetter::ShapeArabic(eastl_size_t iCharBegin, eastl_size_t iCharEnd) { EA_ASSERT(iCharEnd <= mLineLayout.mCharArray.size()); const OTF* const pOTF = mLineLayout.mAnalysisInfoArray[0].mpFont->GetOTF(); // We more or less need to have OpenType font information in order to correctly display Arabic. if(pOTF && pOTF->IsScriptSupported("arab")) { const eastl_size_t iGlyphBegin = mLineLayout.GetGlyphIndexFromCharIndex(iCharBegin); for(eastl_size_t i = iCharBegin, charCount = 0; i < iCharEnd; i += charCount) { const AnalysisInfo* const pAnalysisInfo = &mLineLayout.mAnalysisInfoArray[i]; Char pCharCluster[kMaxArabicCharClusterSize]; eastl_size_t charClusterSize = 0; charCount = GetGeneralCharCluster(i, iCharEnd, pCharCluster, charClusterSize); for(eastl_size_t c = 0, charsEaten = 0, glyphCount = 0, glyphCountPrev = 0; c < charClusterSize; c += charsEaten) { GlyphId pGlyphIdArray[kMaxArabicGlyphClusterSize]; charsEaten = GetGlyphsForChar(pCharCluster + c, charClusterSize - c, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); AppendArabicGlyphCluster(iCharBegin, charCount, pCharCluster + c, charsEaten, pGlyphIdArray + glyphCountPrev, glyphCount - glyphCountPrev, pAnalysisInfo->mnBidiLevel, pOTF); glyphCountPrev = glyphCount; } } // Do OpenType substitutions. FeatureLookupArray featureLookupArray; SetupArabicGsubLookup(featureLookupArray, pOTF); AssignArabicCharProperties(&mLineLayout.mCharArray[iCharBegin], iCharEnd - iCharBegin, &mLineLayout.mGlyphInfoArray[iGlyphBegin]); DoGlyphSubstitution(mLineLayout, iGlyphBegin, featureLookupArray, pOTF); CompleteLineLayoutArrays(iCharBegin, iCharEnd, iGlyphBegin); PlaceGeneralGlyphCluster(iCharBegin, iCharEnd - iCharBegin); } else { // Generic fallback. Arabic text will look wrong, but at least something will be displayed. ShapeGeneral(iCharBegin, iCharEnd); } }
/////////////////////////////////////////////////////////////////////////////// // AppendHebrewCharCluster // eastl_size_t Typesetter::AppendHebrewCharCluster(eastl_size_t iCharBegin, eastl_size_t charCount, const Char* pCharCluster, eastl_size_t clusterSize) { // The 'iCharBegin' argument refers to the position of pCharCluster in mLineLayout.mCharArray. // There is a possibility that pCharCluster doesn't point to mLineLayout.mCharArray[iCharBegin], // as we may be doing some kind of implicit substitution. Thus we have pCharCluster // as an explicit parameter. const AnalysisInfo* const pAnalysisInfo = &mLineLayout.mAnalysisInfoArray[iCharBegin]; GlyphId pGlyphIdArray[kMaxHebrewGlyphClusterSize]; eastl_size_t glyphCount = 0; eastl_size_t glyphCountPrev = 0; eastl_size_t charsEaten = 0; EA_ASSERT(clusterSize && ((iCharBegin + charCount) <= mLineLayout.mCharArray.size())); #ifdef EA_DEBUG memset(pGlyphIdArray, 0, sizeof(pGlyphIdArray)); #endif for(eastl_size_t c = 0; c < clusterSize; c += charsEaten) { if(IsCharHebrew(*pCharCluster)) charsEaten = GetHebrewGlyphsForChars(pCharCluster + c, clusterSize - c, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); else charsEaten = GetGlyphsForChar(pCharCluster + c, clusterSize - c, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); AppendGeneralGlyphCluster(iCharBegin, charCount, pCharCluster + c, charsEaten, pGlyphIdArray + glyphCountPrev, glyphCount - glyphCountPrev, pAnalysisInfo->mnBidiLevel); // Hebrew fonts have decorations that may need to be positionally adjusted. // We could do such adjustments with OpenType or with heuristics. The former // is more involved and slower but results in the best output. The latter // has the opposite characteristics. Right now we do general placement, but // we ought to do some adjustments, such as the case whereby we have lone // diacritics that we added a 0x25CC char to. PlaceGeneralGlyphCluster(iCharBegin + c, charsEaten); glyphCountPrev = glyphCount; } // As it stands now, all chars must result in at least one glyph, though that // one glyph could be a zero-width space character. Note that the render code // has the opportunity to remove such a "no-op" instruction from the display list. EA_ASSERT(glyphCount > 0); return glyphCount; }
/////////////////////////////////////////////////////////////////////////////// // AppendGeneralCharCluster // // charCount is the number of chars referred to in mLineLayout.mCharArray. // clusterSize is the number of chars in pCharCluster, which itself was // generated from mLineLayout.mCharArray and 98% of the time is equivalent. // The strings will be unequal in the case of character-level substitutions // such as mirroring, password chars, control chars, etc. // // The return value is the number of glyphs generated from pCharCluster/clusterSize. // eastl_size_t Typesetter::AppendGeneralCharCluster(eastl_size_t iCharBegin, eastl_size_t charCount, const Char* pCharCluster, eastl_size_t clusterSize) { // The 'iCharBegin' argument refers to the position of pCharCluster in mLineLayout.mCharArray. // There is a possibility that pCharCluster doesn't point to mLineLayout.mCharArray[i], // as we may be doing some kind of implicit substitution. Thus we have pCharCluster // as an explicit parameter. const AnalysisInfo* const pAnalysisInfo = &mLineLayout.mAnalysisInfoArray[iCharBegin]; GlyphId pGlyphIdArray[kMaxGeneralGlyphClusterSize]; eastl_size_t glyphCount = 0; eastl_size_t glyphCountPrev = 0; eastl_size_t charsEaten = 0; EA_ASSERT(clusterSize && ((iCharBegin + charCount) <= mLineLayout.mCharArray.size())); #ifdef EA_DEBUG memset(pGlyphIdArray, 0, sizeof(pGlyphIdArray)); #endif for(eastl_size_t i = 0; i < clusterSize; i += charsEaten) { // GetGlyphsForChar does a simple char -> glyph 1:1 conversion, through pAnalysisInfo->mpFont (mLineLayout.mAnalysisInfoArray[iCharBegin]). charsEaten = GetGlyphsForChar(pCharCluster + i, clusterSize - i, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); EA_ASSERT(charsEaten > 0); AppendGeneralGlyphCluster(iCharBegin, charCount, pCharCluster + i, charsEaten, pGlyphIdArray + glyphCountPrev, glyphCount - glyphCountPrev, pAnalysisInfo->mnBidiLevel); PlaceGeneralGlyphCluster(iCharBegin + i, charsEaten); glyphCountPrev = glyphCount; } // As it stands now, all chars must result in at least one glyph, though that // one glyph could be a zero-width space character. Note that the render code // has the opportunity to remove such a "no-op" instruction from the display list. EA_ASSERT(glyphCount > 0); return glyphCount; }
/////////////////////////////////////////////////////////////////////////////// // GetHebrewGlyphsForChars // // This is the Hebrew version of the general GetGlyphsForChar function, though // it works on multiple chars. It merely does some possible filtering and then // calls GetGlyphsForChar. // eastl_size_t Typesetter::GetHebrewGlyphsForChars(const Char* pChar, eastl_size_t clusterSize, const AnalysisInfo* pAnalysisInfo, GlyphId* pGlyphIdArray, eastl_size_t& glyphCount) { Char charTemp[kMaxHebrewCharClusterSize]; eastl_size_t j, k; glyphCount = 0; // We don't convert non-final and final forms of letters based on the context. // We expect that the user has done that and if they want to use a non-final // character at the end of a word then we let them do so and we just display // that character. This refers to characters such as Kaf, Mem, Nun, and Pe. for(j = 0, k = 0; j < clusterSize; ++j, ++k) { const Char c = pChar[j]; EA_ASSERT(IsCharHebrew(c)); if((j == 0) && (gHebrewCharClass[c - 0x0590] == kHCCNonSpacing)) // If the first char is a decoration char... { // We prepend a character which is a circle implemented with dotted lines. // It is the Unicode standard for generic base character to be represented // by such a dotted circle. It is a defacto standard that we add such a // character when none is provided. It makes the text look more sensible. charTemp[k++] = c; charTemp[k] = 0x25CC; // If we can't use 0x25CC, we ought to try a space char. } else charTemp[k] = c; } for(j = 0; j < k; ) // Now 'k' is the new cluster size, though 99% of the time it will be equal to clusterSize. { j += GetGlyphsForChar(charTemp + j, k - j, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount, L"\x25CB _o", 4); } EA_ASSERT(glyphCount == clusterSize); return glyphCount; }
/////////////////////////////////////////////////////////////////////////////// // GetThaiGlyphs // eastl_size_t Typesetter::GetThaiGlyphs(eastl_size_t i, const Char* pChar, eastl_size_t clusterSize, GlyphId* pGlyphIdArray) { // The 'i' argument refers to the position of pChar in mLineLayout.mCharArray. // There is a possibility that pChar doesn't point to mLineLayout.mCharArray[i], // as we may be doing some kind of implicit substitution. Thus we have pChar // as an explicit parameter. eastl_size_t glyphCount = 0; const AnalysisInfo* const pAnalysisInfo = &mLineLayout.mAnalysisInfoArray[i]; const ThaiCharAdjustment* const pTable = IsCharThai(*pChar) ? &gThaiCharAdjustment : &gLaoCharAdjustment; switch (clusterSize) { case 1: { // Any kind of decoration character must be preceded by a base character. if(IsThaiCharLayoutFlag(pChar[0], tfAV | tfBV | tfTN | tfAD | tfBD | tfAM)) { // We prepend a character which is a circle implemented with dotted lines. // It is the Unicode standard for generic base character to be represented // by such a dotted circle. It is a defacto standard that we prepend such // a character when none is provided. It makes the text look more sensible. const Char cDottedCircle = 0x25CC; // If we can't use 0x25CC, we ought to try a space char. GetGlyphsForChar(&cDottedCircle, 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount, L"\x25CB _o", 4); } GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); break; } case 2: { if(IsThaiCharLayoutFlag(pChar[0], tfNC | tfBC | tfSC) && IsThaiCharLayoutFlag(pChar[1], tfAM)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->mAmComp[0]], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->mAmComp[1]], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfUC) && IsThaiCharLayoutFlag(pChar[1], tfAM)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftleft_tone_ad(pTable->mAmComp[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->mAmComp[1]], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfNC | tfBC | tfSC) && IsThaiCharLayoutFlag(pChar[1], tfAV)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[1])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfNC | tfBC | tfSC) && IsThaiCharLayoutFlag(pChar[1], tfAD | tfTN)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftdown_tone_ad(GetThaiTableIndex(pChar[1]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfUC) && IsThaiCharLayoutFlag(pChar[1], tfAV)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftleft_av(GetThaiTableIndex(pChar[1]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfUC) && IsThaiCharLayoutFlag(pChar[1], tfAD | tfTN)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftdownleft_tone_ad(GetThaiTableIndex(pChar[1]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfNC | tfUC) && IsThaiCharLayoutFlag(pChar[1], tfBV | tfBD)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[1])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfBC) && IsThaiCharLayoutFlag(pChar[1], tfBV | tfBD)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftdown_bv_bd(GetThaiTableIndex(pChar[1]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfSC) && IsThaiCharLayoutFlag(pChar[1], tfBV | tfBD)) { GetGlyphsForChar(&gThaiCharTable[pTable->tailcutcons(GetThaiTableIndex(pChar[0]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[1])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else { // See comments above regarding char 0x25CC. const Char cDottedCircle = 0x25CC; // If we can't use 0x25CC, we ought to try a space char. GetGlyphsForChar(&cDottedCircle, 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount, L"\x25CB _o", 4); GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[1])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } break; } case 3: { if(IsThaiCharLayoutFlag(pChar[0], tfNC | tfBC | tfSC) && IsThaiCharLayoutFlag(pChar[1], tfTN) && IsThaiCharLayoutFlag(pChar[2], tfAM)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->mAmComp[0]], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[1])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->mAmComp[1]], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfUC) && IsThaiCharLayoutFlag(pChar[1], tfTN) && IsThaiCharLayoutFlag(pChar[2], tfAM)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftleft_tone_ad(pTable->mAmComp[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftleft_tone_ad(GetThaiTableIndex(pChar[1]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->mAmComp[1]], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfUC) && IsThaiCharLayoutFlag(pChar[1], tfAV) && IsThaiCharLayoutFlag(pChar[2], tfAD | tfTN)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftleft_av(GetThaiTableIndex(pChar[1]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftleft_tone_ad(GetThaiTableIndex(pChar[2]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfUC) && IsThaiCharLayoutFlag(pChar[1], tfBV) && IsThaiCharLayoutFlag(pChar[2], tfAD | tfTN)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[1])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftdownleft_tone_ad(GetThaiTableIndex(pChar[2]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfNC) && IsThaiCharLayoutFlag(pChar[1], tfBV) && IsThaiCharLayoutFlag(pChar[2], tfAD | tfTN)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[1])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftdown_tone_ad(GetThaiTableIndex(pChar[2]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfSC) && IsThaiCharLayoutFlag(pChar[1], tfBV) && IsThaiCharLayoutFlag(pChar[2], tfAD | tfTN)) { GetGlyphsForChar(&gThaiCharTable[pTable->tailcutcons(GetThaiTableIndex(pChar[0]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[1])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftdown_tone_ad(GetThaiTableIndex(pChar[2]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else if(IsThaiCharLayoutFlag(pChar[0], tfBC) && IsThaiCharLayoutFlag(pChar[1], tfBV) && IsThaiCharLayoutFlag(pChar[2], tfAD | tfTN)) { GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[0])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftdown_bv_bd(GetThaiTableIndex(pChar[1]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); GetGlyphsForChar(&gThaiCharTable[pTable->shiftdown_tone_ad(GetThaiTableIndex(pChar[2]))], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else { for(eastl_size_t i = 0; i < 3; i++) GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[i])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } } break; case 4: default: { // Call ourselves recursively for the first three glyphs, then manually add on // any trailing glyphs without any selection/placement intelligence. glyphCount = GetThaiGlyphs(i, pChar, 3, pGlyphIdArray); for(eastl_size_t i = 3; i < clusterSize; i++) GetGlyphsForChar(&gThaiCharTable[GetThaiTableIndex(pChar[i])], 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); break; } } return glyphCount; }
/////////////////////////////////////////////////////////////////////////////// // AppendHangulCharCluster // eastl_size_t Typesetter::AppendHangulCharCluster(eastl_size_t iCharBegin, eastl_size_t charCount, const Char* pCharCluster, eastl_size_t clusterSize, int clusterType) { // The 'iCharBegin' argument refers to the position of pCharCluster in mLineLayout.mCharArray. // There is a possibility that pCharCluster doesn't point to mLineLayout.mCharArray[iCharBegin], // as we may be doing some kind of implicit substitution. Thus we have pCharCluster // as an explicit parameter. const AnalysisInfo* const pAnalysisInfo = &mLineLayout.mAnalysisInfoArray[iCharBegin]; GlyphId pGlyphIdArray[kMaxHangulGlyphClusterSize]; eastl_size_t glyphCount = 0; eastl_size_t charsEaten = 0; // Number of chars read from pCharCluster. EA_ASSERT(clusterSize && ((iCharBegin + charCount) <= mLineLayout.mCharArray.size())); #ifdef EA_DEBUG memset(pGlyphIdArray, 0, sizeof(pGlyphIdArray)); #endif switch(clusterType) { case kHangulClusterTypeJamo: { const Char cTone = pCharCluster[clusterSize - 1]; const bool bAppendTone = IsTone(cTone); int syllableLength; Char c; if(bAppendTone) --clusterSize; // Pretend it isn't there for now. if((clusterSize >= 3) && IsLSyllable(pCharCluster[0]) && IsVSyllable(pCharCluster[1]) && IsTSyllable(pCharCluster[2])) syllableLength = 3; else if((clusterSize >= 2) && IsLSyllable(pCharCluster[0]) && IsVSyllable(pCharCluster[1])) syllableLength = 2; else syllableLength = 0; if(syllableLength) { if(syllableLength == 3) c = GetSyllableLVT(pCharCluster[0], pCharCluster[1], pCharCluster[2]); else c = GetSyllableLV(pCharCluster[0], pCharCluster[1]); GetGlyphsForChar(&c, 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); charsEaten += syllableLength; } // If there are any other chars to shape (usually there aren't), just append them. for(eastl_size_t i = charsEaten; i < clusterSize; i += charsEaten) { if((pCharCluster[i] != kJamoLFiller) && // Outright ignore filler chars. (pCharCluster[i] != kJamoVFiller)) { // The font selection in use had better have the desired glyphs or // else we will be displaying a lot of square boxes on the screen. charsEaten += GetGlyphsForChar(pCharCluster + i, clusterSize - i, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } else ++charsEaten; } EA_ASSERT(glyphCount > 0); // Do we have just a single Jamo filler char? if(glyphCount == 0) { const Char cHangulFiller = 0x3164; // 0x3164 is the Hangul filler char. It's just blank like a space char. charsEaten += GetGlyphsForChar(&cHangulFiller, 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount, L"\x25CB _o", 4); } if(bAppendTone) { charsEaten += GetGlyphsForChar(&cTone, 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount, (cTone == kHangulTone1) ? L"\x00b7" : L":", 1); } break; } case kHangulClusterTypeTone: // Standalone tone with no preceeding base glyph. { // We prepend a character which is a circle implemented with dotted lines. // It is the Unicode standard for generic base character to be represented // by such a dotted circle. It is a defacto standard that we prepend such // a character when none is provided. It makes the text look more sensible. const Char cDottedCircle = 0x25CC; for(eastl_size_t i = charsEaten; i < clusterSize; i += charsEaten) // clusterSize should usually (always?) be just one. { // Note that in practice we're going to need to put the tone to the // left of the glyph, though here we stuff it afterwards. What we have // here won't do. Also, the tone chars ideally have a zero advance, // but the fallbacks definitely do not. // We draw the tone, using fallback characters if the tone char isn't present. charsEaten += GetGlyphsForChar(pCharCluster + i, clusterSize - i, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount, (pCharCluster[i] == kHangulTone1) ? L"\x00b7" : L":", 1); // Append a dotted-circle glyph, with a fallback of some other glyphs. GetGlyphsForChar(&cDottedCircle, 1, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount, L"\x25CB _o", 4); } break; } case kHangulClusterTypeUnicode: { // We just convert the Unicode Chars to glyphs. for(eastl_size_t i = charsEaten; i < clusterSize; i += charsEaten) { charsEaten += GetGlyphsForChar(pCharCluster + i, clusterSize - i, pAnalysisInfo, pGlyphIdArray + glyphCount, glyphCount); } break; } default: EA_FAIL_MESSAGE("Typesetter::AppendHangulCharCluster: Unknown cluster type."); break; } // Assert that all of the passed in cluster was processed. EA_ASSERT(charsEaten == clusterSize); // We use the general glyph append function. AppendGeneralGlyphCluster(iCharBegin, charCount, pCharCluster, charsEaten, pGlyphIdArray, glyphCount, pAnalysisInfo->mnBidiLevel); // We use the general glyph placement function. PlaceGeneralGlyphCluster(iCharBegin, charsEaten); // As it stands now, all chars must result in at least one glyph, though that // one glyph could be a zero-width space character. Note that the render code // has the opportunity to remove such a "no-op" instruction from the display list. EA_ASSERT(glyphCount > 0); return glyphCount; }