/////////////////////////////////////////////////////////////////////////////// // 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; }
/////////////////////////////////////////////////////////////////////////////// // 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; }
/////////////////////////////////////////////////////////////////////////////// // 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; }