Example #1
0
///////////////////////////////////////////////////////////////////////////////
// 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);
    }
}
Example #3
0
///////////////////////////////////////////////////////////////////////////////
// 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;
}
Example #5
0
///////////////////////////////////////////////////////////////////////////////
// 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;
}
Example #6
0
///////////////////////////////////////////////////////////////////////////////
// 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;
}
Example #7
0
///////////////////////////////////////////////////////////////////////////////
// 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;
}